//////////////////////////////////////////////////////////////////////////////// /// @brief Basic query data structures /// /// @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 #include "VocBase/query-join.h" #include "VocBase/query-base.h" #include "VocBase/query-parse.h" #include "VocBase/query-locks.h" //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Hash function used to hash bind parameters //////////////////////////////////////////////////////////////////////////////// static uint64_t HashBindParameter (TRI_associative_pointer_t* array, void const* element) { TRI_bind_parameter_t* parameter = (TRI_bind_parameter_t*) element; return TRI_FnvHashString(parameter->_name); } //////////////////////////////////////////////////////////////////////////////// /// @brief Comparison function used to determine bind parameter equality //////////////////////////////////////////////////////////////////////////////// static bool EqualBindParameter (TRI_associative_pointer_t* array, void const* key, void const* element) { TRI_bind_parameter_t* parameter = (TRI_bind_parameter_t*) element; return TRI_EqualString(key, parameter->_name); } //////////////////////////////////////////////////////////////////////////////// /// @brief Free all bind parameters in associative array //////////////////////////////////////////////////////////////////////////////// static void FreeBindParameters (TRI_associative_pointer_t* array) { TRI_bind_parameter_t* parameter; size_t i; for (i = 0; i < array->_nrAlloc; i++) { parameter = (TRI_bind_parameter_t*) array->_table[i]; if (parameter) { TRI_FreeBindParameter(parameter); } } } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the select part //////////////////////////////////////////////////////////////////////////////// static bool InitSelectQueryTemplate (TRI_query_template_t* const template_) { QL_ast_query_select_t* select_; char* primaryAlias; select_ = &template_->_query->_select; QLOptimizeExpression(select_->_base); primaryAlias = QLAstQueryGetPrimaryAlias(template_->_query); select_->_type = QLOptimizeGetSelectType(select_->_base, primaryAlias); select_->_usesBindParameters = QLOptimizeUsesBindParameters(select_->_base); select_->_functionCode = NULL; select_->_isConstant = false; if (select_->_type == QLQuerySelectTypeEvaluated && !select_->_usesBindParameters) { // create JS code select_->_functionCode = TRI_GetFunctionCodeQueryJavascript( select_->_base, &template_->_bindParameters ); if (!select_->_functionCode) { TRI_SetQueryError(&template_->_error, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } TRI_RegisterStringQuery(&template_->_memory._strings, select_->_functionCode); if (QLOptimizeIsConst(select_->_base)) { // select produces constant documents select_->_isConstant = true; } } LOG_DEBUG("created select part of query template. type: %i, " "usesBindParameters: %i functionCode: %s", (int) select_->_type, (int) select_->_usesBindParameters, (select_->_functionCode ? select_->_functionCode : "")); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the where part //////////////////////////////////////////////////////////////////////////////// static bool InitWhereQueryTemplate (TRI_query_template_t* const template_) { QL_ast_query_where_t* where; where = &template_->_query->_where; QLOptimizeExpression(where->_base); where->_type = QLOptimizeGetWhereType(where->_base); where->_usesBindParameters = QLOptimizeUsesBindParameters(where->_base); where->_functionCode = NULL; if (where->_type == QLQueryWhereTypeMustEvaluate && !where->_usesBindParameters) { // create JS code where->_functionCode = TRI_GetFunctionCodeQueryJavascript( where->_base, &template_->_bindParameters ); if (!where->_functionCode) { TRI_SetQueryError(&template_->_error, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } TRI_RegisterStringQuery(&template_->_memory._strings, where->_functionCode); } LOG_DEBUG("created where part of query template. type: %i, " "usesBindParameters: %i, functionCode: %s", (int) where->_type, (int) where->_usesBindParameters, (where->_functionCode ? where->_functionCode : "")); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the order part //////////////////////////////////////////////////////////////////////////////// static bool InitOrderQueryTemplate (TRI_query_template_t* const template_) { QL_ast_query_order_t* order; // optimize order clause order = &template_->_query->_order; order->_type = QLQueryOrderTypeNone; order->_usesBindParameters = false; order->_functionCode = NULL; if (order->_base) { QLOptimizeOrder(order->_base); order->_type = QLOptimizeGetOrderType(order->_base); order->_usesBindParameters = QLOptimizeUsesBindParameters(order->_base); if (order->_type == QLQueryOrderTypeMustEvaluate && !order->_usesBindParameters) { // create JS code order->_functionCode = TRI_GetOrderFunctionCodeQueryJavascript( order->_base, &template_->_bindParameters ); if (!order->_functionCode) { TRI_SetQueryError(&template_->_error, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } TRI_RegisterStringQuery(&template_->_memory._strings, order->_functionCode); } } LOG_DEBUG("created order part of query template. type: %i, " "usesBindParameters: %i, functionCode: %s", (int) order->_type, (int) order->_usesBindParameters, (order->_functionCode ? order->_functionCode : "")); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the from part //////////////////////////////////////////////////////////////////////////////// static bool InitFromQueryTemplate (TRI_query_template_t* const template_) { TRI_query_node_t* node; assert(template_); assert(template_->_query); node = template_->_query->_from._base->_next; // iterate over all joins while (node) { if (node->_rhs && TRI_QueryNodeGetTypeGroup(node->_type) == TRI_QueryNodeGroupJoin) { QL_ast_query_collection_t* collection; char* alias; alias = node->_lhs->_rhs->_value._stringValue; assert(alias); collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&template_->_query->_from._collections, alias); assert(collection); // optimize on clause QLOptimizeExpression(node->_rhs); collection->_where._base = node->_rhs; collection->_where._type = QLOptimizeGetWhereType(node->_rhs); collection->_where._usesBindParameters = QLOptimizeUsesBindParameters(node->_rhs); if (collection->_where._type == QLQueryWhereTypeMustEvaluate && !collection->_where._usesBindParameters) { // create JS code collection->_where._functionCode = TRI_GetFunctionCodeQueryJavascript( node->_rhs, &template_->_bindParameters ); if (!collection->_where._functionCode) { TRI_SetQueryError(&template_->_error, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } TRI_RegisterStringQuery(&template_->_memory._strings, collection->_where._functionCode); } LOG_DEBUG("created from part of query template. alias: %s, name: %s, " "type: %i, usesBindParameters: %i, functionCode: %s", collection->_alias, collection->_name, (int) collection->_where._type, (int) collection->_where._usesBindParameters, (collection->_where._functionCode ? collection->_where._functionCode : "")); } node = node->_next; } if (template_->_query->_from._collections._nrUsed > QUERY_MAX_JOINS) { TRI_SetQueryError(&template_->_error, TRI_ERROR_QUERY_TOO_MANY_JOINS, NULL); return false; } QLOptimizeFrom(template_->_query); return true; } // ----------------------------------------------------------------------------- // --SECTION-- bind parameters // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Get the names of all bind parameters //////////////////////////////////////////////////////////////////////////////// TRI_vector_string_t TRI_GetNamesBindParameter (TRI_associative_pointer_t* const parameters) { TRI_vector_string_t names; size_t i; TRI_InitVectorString(&names); assert(parameters); // enumerate all bind parameters.... for (i = 0; i < parameters->_nrAlloc; i++) { TRI_bind_parameter_t* parameter; char* copy; parameter = (TRI_bind_parameter_t*) parameters->_table[i]; if (!parameter) { continue; } copy = TRI_DuplicateString(parameter->_name); if (copy) { TRI_PushBackVectorString(&names, copy); } } return names; } //////////////////////////////////////////////////////////////////////////////// /// @brief Free a single bind parameter //////////////////////////////////////////////////////////////////////////////// void TRI_FreeBindParameter (TRI_bind_parameter_t* const parameter) { assert(parameter); if (parameter->_name) { TRI_Free(parameter->_name); } if (parameter->_data) { TRI_FreeJson(parameter->_data); } TRI_Free(parameter); } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a bind parameter //////////////////////////////////////////////////////////////////////////////// TRI_bind_parameter_t* TRI_CreateBindParameter (const char* name, const TRI_json_t* data) { TRI_bind_parameter_t* parameter; assert(name); parameter = (TRI_bind_parameter_t*) TRI_Allocate(sizeof(TRI_bind_parameter_t)); if (!parameter) { return NULL; } parameter->_name = TRI_DuplicateString(name); if (!parameter->_name) { TRI_Free(parameter); return NULL; } parameter->_data = NULL; if (data) { parameter->_data = TRI_CopyJson((TRI_json_t*) data); if (!parameter->_data) { TRI_Free(parameter->_name); TRI_Free(parameter); return NULL; } } return parameter; } // ----------------------------------------------------------------------------- // --SECTION-- query template // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Initialize the structs contained in a query template and perform /// some basic optimizations and type detections //////////////////////////////////////////////////////////////////////////////// bool TRI_InitQueryTemplate (TRI_query_template_t* const template_) { return InitSelectQueryTemplate(template_) && InitWhereQueryTemplate(template_) && InitOrderQueryTemplate(template_) && InitFromQueryTemplate(template_); } //////////////////////////////////////////////////////////////////////////////// /// @brief Add a bind parameter to a query template //////////////////////////////////////////////////////////////////////////////// bool TRI_AddBindParameterQueryTemplate (TRI_query_template_t* const template_, const TRI_bind_parameter_t* const parameter) { assert(template_); if (!parameter) { TRI_SetQueryError(&template_->_error, TRI_ERROR_OUT_OF_MEMORY, NULL); } assert(parameter->_name); // bind parameter redeclared if (TRI_LookupByKeyAssociativePointer(&template_->_bindParameters, parameter->_name)) { TRI_FreeBindParameter((TRI_bind_parameter_t* const) parameter); return false; } TRI_InsertKeyAssociativePointer(&template_->_bindParameters, parameter->_name, (void*) parameter, true); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a query template //////////////////////////////////////////////////////////////////////////////// TRI_query_template_t* TRI_CreateQueryTemplate (const char* queryString, const TRI_vocbase_t* const vocbase) { TRI_query_template_t* template_; assert(queryString); assert(vocbase); template_ = (TRI_query_template_t*) TRI_Allocate(sizeof(TRI_query_template_t)); if (!template_) { return NULL; } template_->_queryString = TRI_DuplicateString(queryString); if (!template_->_queryString) { TRI_Free(template_); return NULL; } template_->_query = (QL_ast_query_t*) TRI_Allocate(sizeof(QL_ast_query_t)); if (!template_->_query) { TRI_Free(template_->_queryString); TRI_Free(template_); return NULL; } template_->_vocbase = (TRI_vocbase_t*) vocbase; TRI_InitQueryError(&template_->_error); TRI_InitAssociativePointer(&template_->_bindParameters, TRI_HashStringKeyAssociativePointer, HashBindParameter, EqualBindParameter, 0); // init vectors needed for book-keeping memory TRI_InitVectorPointer(&template_->_memory._nodes); TRI_InitVectorPointer(&template_->_memory._strings); TRI_InitVectorPointer(&template_->_memory._listHeads); TRI_InitVectorPointer(&template_->_memory._listTails); QLAstQueryInit(template_->_query); TRI_InitMutex(&template_->_lock); LOG_DEBUG("created query template for query %s", queryString); return template_; } //////////////////////////////////////////////////////////////////////////////// /// @brief Free a query template //////////////////////////////////////////////////////////////////////////////// void TRI_FreeQueryTemplate (TRI_query_template_t* template_) { assert(template_); assert(template_->_queryString); assert(template_->_query); QLAstQueryFree(template_->_query); TRI_Free(template_->_query); // list elements in _listHeads and _listTails must not be freed separately as // they are AstNodes handled by _nodes already // free vectors themselves TRI_FreeNodeVectorQuery(&template_->_memory._nodes); TRI_FreeStringVectorQuery(&template_->_memory._strings); TRI_DestroyVectorPointer(&template_->_memory._listHeads); TRI_DestroyVectorPointer(&template_->_memory._listTails); TRI_Free(template_->_queryString); TRI_FreeQueryError(&template_->_error); FreeBindParameters(&template_->_bindParameters); TRI_DestroyAssociativePointer(&template_->_bindParameters); TRI_DestroyMutex(&template_->_lock); TRI_Free(template_); LOG_DEBUG("destroyed query template"); } // ----------------------------------------------------------------------------- // --SECTION-- query instance // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief get size of extra data in geo join parts //////////////////////////////////////////////////////////////////////////////// static size_t GetJoinPartExtraDataSizeGeo (TRI_join_part_t* part) { return sizeof(double); } //////////////////////////////////////////////////////////////////////////////// /// @brief Free join part memory //////////////////////////////////////////////////////////////////////////////// static void FreeJoinPart (TRI_join_part_t* part) { if (part->_alias) { TRI_Free(part->_alias); } if (part->_ranges) { QLOptimizeFreeRangeVector(part->_ranges); TRI_Free(part->_ranges); } if (part->_collectionName) { TRI_Free(part->_collectionName); } if (part->_feeder) { part->_feeder->free(part->_feeder); } if (part->_context) { TRI_FreeExecutionContext(part->_context); } if (part->_listDocuments._buffer) { TRI_DestroyVectorPointer(&part->_listDocuments); } TRI_Free(part); } //////////////////////////////////////////////////////////////////////////////// /// @brief Add a part to a select join //////////////////////////////////////////////////////////////////////////////// static bool AddJoinPartQueryInstance (TRI_query_instance_t* instance, const TRI_join_type_e type, QL_ast_query_where_t* where, TRI_vector_pointer_t* ranges, char* collectionName, char* alias, QL_ast_query_geo_restriction_t* geoRestriction) { TRI_join_part_t* part; QL_ast_query_collection_t* collection; part = (TRI_join_part_t*) TRI_Allocate(sizeof(TRI_join_part_t)); if (!part) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&instance->_query._from._collections, alias); assert(collection); // initialize vector for list joins TRI_InitVectorPointer(&part->_listDocuments); part->_context = NULL; part->_singleDocument = NULL; part->_feeder = NULL; part->_type = type; part->_where = where; part->_ranges = ranges; part->_collection = NULL; part->_collectionName = TRI_DuplicateString(collectionName); part->_alias = TRI_DuplicateString(alias); part->_geoRestriction = geoRestriction; part->_mustMaterialize._select = (collection->_refCount._select > 0); part->_mustMaterialize._where = (collection->_refCount._where > 0); part->_mustMaterialize._order = (collection->_refCount._order > 0); part->_mustMaterialize._join = (collection->_refCount._join > 0); if (!part->_collectionName || !part->_alias) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); part->free(part); return false; } // determine size of extra data to store if (part->_geoRestriction) { part->_extraData._size = GetJoinPartExtraDataSizeGeo(part); part->_extraData._alias = part->_geoRestriction->_alias; } else { part->_extraData._size = 0; part->_extraData._alias = NULL; } part->free = FreeJoinPart; if (part->_where != NULL && part->_where->_type == QLQueryWhereTypeMustEvaluate) { assert(part->_where->_functionCode); part->_context = TRI_CreateExecutionContext(part->_where->_functionCode); if (!part->_context) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); part->free(part); return false; } } if (part->_where) { LOG_DEBUG("added join part to query instance. alias: %s, name: %s, " "type: %i, functionCode: %s", part->_alias, part->_collectionName, (int) part->_where->_type, (part->_where->_functionCode ? part->_where->_functionCode : "")); } else { LOG_DEBUG("added join part to query instance. alias: %s, name: %s", part->_alias, part->_collectionName); } TRI_PushBackVectorPointer(&instance->_join, part); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the select part of a query instance //////////////////////////////////////////////////////////////////////////////// static bool InitSelectQueryInstance (TRI_query_instance_t* const instance) { QL_ast_query_select_t* queryTemplate = &instance->_template->_query->_select; QL_ast_query_select_t* queryInstance = &instance->_query._select; // clone original values queryInstance->_base = queryTemplate->_base; queryInstance->_type = queryTemplate->_type; queryInstance->_usesBindParameters = queryTemplate->_usesBindParameters; queryInstance->_functionCode = queryTemplate->_functionCode; queryInstance->_isConstant = queryTemplate->_isConstant; if (queryInstance->_usesBindParameters) { TRI_query_node_t* copy; char* functionCode; // if bind parameters are used, copy part and re-optimize assert(queryTemplate->_base); copy = TRI_CopyQueryPartQueryInstance(instance, queryTemplate->_base); if (!copy) { return false; } queryInstance->_base = copy; QLOptimizeExpression(queryInstance->_base); // re-create function code functionCode = TRI_GetFunctionCodeQueryJavascript( queryInstance->_base, &instance->_bindParameters ); if (functionCode) { TRI_RegisterStringQuery(&instance->_memory._strings, functionCode); queryInstance->_functionCode = functionCode; } else { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } } LOG_DEBUG("added select part to query instance. type: %i, functionCode: %s", (int) queryInstance->_type, (queryInstance->_functionCode ? queryInstance->_functionCode : "")); if (queryInstance->_isConstant) { LOG_DEBUG("select expression is constant"); } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the where part of a query instance //////////////////////////////////////////////////////////////////////////////// static bool InitWhereQueryInstance (TRI_query_instance_t* const instance) { QL_ast_query_where_t* queryTemplate = &instance->_template->_query->_where; QL_ast_query_where_t* queryInstance = &instance->_query._where; // clone original values queryInstance->_base = queryTemplate->_base; queryInstance->_type = queryTemplate->_type; queryInstance->_usesBindParameters = queryTemplate->_usesBindParameters; queryInstance->_functionCode = queryTemplate->_functionCode; queryInstance->_context = NULL; if (queryInstance->_usesBindParameters) { TRI_query_node_t* copy; // if bind parameters are used, copy part and re-optimize assert(queryTemplate->_base); copy = TRI_CopyQueryPartQueryInstance(instance, queryTemplate->_base); if (!copy) { return false; } queryInstance->_base = copy; QLOptimizeExpression(queryInstance->_base); queryInstance->_type = QLOptimizeGetWhereType(queryInstance->_base); if (queryInstance->_type == QLQueryWhereTypeMustEvaluate) { char* functionCode; // re-create function code functionCode = TRI_GetFunctionCodeQueryJavascript( queryInstance->_base, &instance->_bindParameters ); if (functionCode) { TRI_RegisterStringQuery(&instance->_memory._strings, functionCode); queryInstance->_functionCode = functionCode; } else { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } } } if (queryInstance->_functionCode) { queryInstance->_context = TRI_CreateExecutionContext(queryInstance->_functionCode); if (!queryInstance->_context) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } } LOG_DEBUG("added where part to query instance. type: %i, functionCode: %s", (int) queryInstance->_type, (queryInstance->_functionCode ? queryInstance->_functionCode : "")); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the order part of a query instance //////////////////////////////////////////////////////////////////////////////// static bool InitOrderQueryInstance (TRI_query_instance_t* const instance) { QL_ast_query_order_t* queryTemplate = &instance->_template->_query->_order; QL_ast_query_order_t* queryInstance = &instance->_query._order; // clone original values queryInstance->_base = queryTemplate->_base; queryInstance->_type = queryTemplate->_type; queryInstance->_usesBindParameters = queryTemplate->_usesBindParameters; queryInstance->_functionCode = queryTemplate->_functionCode; if (queryTemplate->_usesBindParameters) { TRI_query_node_t* copy; // if bind parameters are used, copy part and re-optimize assert(queryTemplate->_base); copy = TRI_CopyQueryPartQueryInstance(instance, queryTemplate->_base); if (!copy) { return false; } queryInstance->_base = copy; QLOptimizeOrder(queryInstance->_base); queryInstance->_type = QLOptimizeGetOrderType(queryInstance->_base); if (queryInstance->_type == QLQueryOrderTypeMustEvaluate) { char* functionCode; // re-create function code functionCode = TRI_GetOrderFunctionCodeQueryJavascript( queryInstance->_base, &instance->_bindParameters ); if (functionCode) { TRI_RegisterStringQuery(&instance->_memory._strings, functionCode); queryInstance->_functionCode = functionCode; } else { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } } } LOG_DEBUG("added order part to query instance. type: %i, functionCode: %s", (int) queryInstance->_type, (queryInstance->_functionCode ? queryInstance->_functionCode : "")); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the limit part of a query instance //////////////////////////////////////////////////////////////////////////////// static bool InitLimitQueryInstance (TRI_query_instance_t* const instance) { QL_ast_query_limit_t* queryTemplate = &instance->_template->_query->_limit; QL_ast_query_limit_t* queryInstance = &instance->_query._limit; // clone original values queryInstance->_offset = queryTemplate->_offset; queryInstance->_count = queryTemplate->_count; queryInstance->_isUsed = queryTemplate->_isUsed; if (!queryInstance->_isUsed) { queryInstance->_offset = 0; queryInstance->_count = (TRI_voc_ssize_t) INT32_MAX; } LOG_DEBUG("added limit part to query instance. offset: %lu, count: %li", (unsigned long) queryInstance->_offset, (unsigned int) queryInstance->_count); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init the from part of a query instance //////////////////////////////////////////////////////////////////////////////// static bool InitFromQueryInstance (TRI_query_instance_t* const instance) { QL_ast_query_from_t* queryTemplate = &instance->_template->_query->_from; size_t i; instance->_query._from._base = queryTemplate->_base; // clone original values for (i = 0; i < queryTemplate->_collections._nrAlloc; i++) { QL_ast_query_collection_t* source; QL_ast_query_collection_t* collection; source = (QL_ast_query_collection_t*) queryTemplate->_collections._table[i]; if (!source) { continue; } collection = (QL_ast_query_collection_t*) TRI_Allocate(sizeof(QL_ast_query_collection_t)); if (!collection) { return false; } collection->_name = source->_name; collection->_alias = source->_alias; collection->_isPrimary = source->_isPrimary; collection->_declarationOrder = source->_declarationOrder; collection->_geoRestriction = source->_geoRestriction; collection->_where._base = source->_where._base; collection->_where._type = source->_where._type; collection->_where._usesBindParameters = source->_where._usesBindParameters; collection->_where._functionCode = source->_where._functionCode; collection->_refCount = source->_refCount; if (source->_where._usesBindParameters) { TRI_query_node_t* copy; assert(source->_where._base); copy = TRI_CopyQueryPartQueryInstance(instance, source->_where._base); if (!copy) { TRI_Free(collection); TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } collection->_where._base = copy; QLOptimizeExpression(collection->_where._base); collection->_where._type = QLOptimizeGetWhereType(collection->_where._base); if (collection->_where._type == QLQueryWhereTypeMustEvaluate) { char* functionCode; // re-create function code functionCode = TRI_GetFunctionCodeQueryJavascript( collection->_where._base, &instance->_bindParameters ); if (functionCode) { TRI_RegisterStringQuery(&instance->_memory._strings, functionCode); collection->_where._functionCode = functionCode; } else { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } } } TRI_InsertKeyAssociativePointer(&instance->_query._from._collections, collection->_alias, (void*) collection, true); LOG_DEBUG("added join part to query instance. alias: %s, name: %s, " "type: %i, usesBindParameters: %i, functionCode: %s", collection->_alias, collection->_name, (int) collection->_where._type, (int) collection->_where._usesBindParameters, (collection->_where._functionCode ? collection->_where._functionCode : "")); } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Init query parts of the instance /// /// If the query uses bind parameters, the query parts with bind parameters are /// copied, re-optimized, and the Javascript functions for these parts are /// re-created. //////////////////////////////////////////////////////////////////////////////// static bool InitPartsQueryInstance (TRI_query_instance_t* const instance) { QL_ast_query_t* queryTemplate; QL_ast_query_t* queryInstance; assert(instance); assert(instance->_template); assert(instance->_template->_query); queryTemplate = instance->_template->_query; queryInstance = &instance->_query; queryInstance->_isEmpty = queryTemplate->_isEmpty; if (!InitSelectQueryInstance(instance)) { return false; } if (!InitWhereQueryInstance(instance)) { return false; } if (!InitOrderQueryInstance(instance)) { return false; } if (!InitLimitQueryInstance(instance)) { return false; } if (!InitFromQueryInstance(instance)) { return false; } if (queryInstance->_where._type == QLQueryWhereTypeAlwaysFalse) { // query will never have any results due to where clause queryInstance->_isEmpty = true; } if (queryInstance->_limit._count == 0) { // query will never have any results due to limit clause queryInstance->_isEmpty = true; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Get join type from a join node //////////////////////////////////////////////////////////////////////////////// static TRI_join_type_e GetJoinTypeFromNode (const TRI_query_node_t* const node) { switch (node->_type) { case TRI_QueryNodeJoinList: return JOIN_TYPE_LIST; case TRI_QueryNodeJoinInner: return JOIN_TYPE_INNER; case TRI_QueryNodeJoinLeft: return JOIN_TYPE_OUTER; default: assert(false); } assert(false); return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief Add primary collection to join //////////////////////////////////////////////////////////////////////////////// static bool AddJoinPrimaryCollection (TRI_query_instance_t* const instance, const TRI_query_node_t* const node) { TRI_vector_pointer_t* ranges; QL_ast_query_collection_t* collection; char* collectionName; char* collectionAlias; assert(instance); assert(node); assert(node->_lhs); assert(node->_rhs); collectionName = node->_lhs->_value._stringValue; collectionAlias = node->_rhs->_value._stringValue; collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&instance->_query._from._collections, collectionAlias); assert(collection); ranges = NULL; if (!collection->_geoRestriction) { ranges = QLOptimizeRanges(instance->_query._where._base, &instance->_bindParameters); } return AddJoinPartQueryInstance(instance, JOIN_TYPE_PRIMARY, NULL, ranges, collectionName, collectionAlias, QLAstQueryCloneRestriction(collection->_geoRestriction)); } //////////////////////////////////////////////////////////////////////////////// /// @brief Add another (non-primary) collection to join //////////////////////////////////////////////////////////////////////////////// static bool AddJoinSecondaryCollection (TRI_query_instance_t* const instance, const TRI_query_node_t* const node) { TRI_vector_pointer_t* ranges; QL_ast_query_collection_t* collection; QL_ast_query_where_type_e conditionType; char* collectionName; char* collectionAlias; assert(instance); assert(node); collectionName = node->_lhs->_lhs->_value._stringValue; collectionAlias = node->_lhs->_rhs->_value._stringValue; collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&instance->_query._from._collections, collectionAlias); assert(collection); ranges = NULL; conditionType = collection->_where._type; if (conditionType == QLQueryWhereTypeMustEvaluate) { // join condition must be evaluated for each result if (!collection->_geoRestriction) { ranges = QLOptimizeRanges(node->_rhs, &instance->_bindParameters); } } return AddJoinPartQueryInstance(instance, GetJoinTypeFromNode(node), &collection->_where, ranges, collectionName, collectionAlias, QLAstQueryCloneRestriction(collection->_geoRestriction)); } //////////////////////////////////////////////////////////////////////////////// /// @brief Set up the join part of the query //////////////////////////////////////////////////////////////////////////////// static bool InitJoinsQueryInstance (TRI_query_instance_t* const instance) { TRI_query_node_t* node; assert(instance); assert(instance->_query._from._base); node = instance->_query._from._base->_next; assert(node); AddJoinPrimaryCollection(instance, node); node = node->_next; while (node) { if (!AddJoinSecondaryCollection(instance, node)) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } node = node->_next; } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief free join parts used in a query //////////////////////////////////////////////////////////////////////////////// static void FreeJoinsQueryInstance (TRI_query_instance_t* const instance) { size_t i; for (i = 0; i < instance->_join._length; i++) { TRI_join_part_t* part; part = (TRI_join_part_t*) instance->_join._buffer[i]; assert(part); part->free(part); } TRI_DestroyVectorPointer(&instance->_join); } //////////////////////////////////////////////////////////////////////////////// /// @brief Add bind parameter values //////////////////////////////////////////////////////////////////////////////// static bool AddBindParameterValues (TRI_query_instance_t* const instance, const TRI_json_t* parameters) { size_t i; assert(parameters); if (parameters->_type != TRI_JSON_ARRAY) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_QUERY_BIND_PARAMETER_MISSING, "global"); return false; } for (i = 0; i < parameters->_value._objects._length; i += 2) { void* nameParameter; void* valueParameter; TRI_bind_parameter_t* parameter; nameParameter = TRI_AtVector(¶meters->_value._objects, i); valueParameter = TRI_AtVector(¶meters->_value._objects, i + 1); parameter = TRI_CreateBindParameter(((TRI_json_t*) nameParameter)->_value._string.data, (TRI_json_t*) valueParameter); if (!parameter) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return false; } if (TRI_LookupByKeyAssociativePointer(&instance->_bindParameters, parameter->_name)) { TRI_FreeBindParameter(parameter); // redeclaration of bind parameter TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_QUERY_BIND_PARAMETER_REDECLARED, parameter->_name); return false; } TRI_AddBindParameterQueryInstance(instance, parameter); // check if template has such bind parameter if (!TRI_LookupByKeyAssociativePointer(&instance->_template->_bindParameters, parameter->_name)) { // invalid bind parameter TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_QUERY_BIND_PARAMETER_UNDECLARED, parameter->_name); return false; } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Validate bind parameter values //////////////////////////////////////////////////////////////////////////////// static bool ValidateBindParameters (TRI_query_instance_t* const instance) { TRI_associative_pointer_t* templateParameters; TRI_associative_pointer_t* instanceParameters; size_t i; templateParameters = &instance->_template->_bindParameters; instanceParameters = &instance->_bindParameters; // enumerate all template bind parameters.... for (i = 0; i < templateParameters->_nrAlloc; i++) { TRI_bind_parameter_t* parameter; parameter = (TRI_bind_parameter_t*) templateParameters->_table[i]; if (!parameter) { continue; } // ... and check if the parameter is also defined for the instance if (!TRI_LookupByKeyAssociativePointer(instanceParameters, parameter->_name)) { // invalid bind parameter TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_QUERY_BIND_PARAMETER_MISSING, parameter->_name); return false; } } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Set the value of a bind parameter //////////////////////////////////////////////////////////////////////////////// bool TRI_AddBindParameterQueryInstance (TRI_query_instance_t* const instance, const TRI_bind_parameter_t* const parameter) { assert(instance); assert(parameter); assert(parameter->_name); assert(parameter->_data); // check if template has bind parameter defined if (!TRI_LookupByKeyAssociativePointer(&instance->_template->_bindParameters, parameter->_name)) { // invalid bind parameter TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_QUERY_BIND_PARAMETER_UNDECLARED, parameter->_name); return false; } // check if bind parameter is already defined for instance if (TRI_LookupByKeyAssociativePointer(&instance->_bindParameters, parameter->_name)) { // bind parameter defined => duplicate definition TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_QUERY_BIND_PARAMETER_REDECLARED, parameter->_name); return false; } TRI_InsertKeyAssociativePointer(&instance->_bindParameters, parameter->_name, (void*) parameter, true); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Free a query instance //////////////////////////////////////////////////////////////////////////////// void TRI_FreeQueryInstance (TRI_query_instance_t* const instance) { assert(instance); assert(instance->_template); TRI_FreeQueryError(&instance->_error); FreeBindParameters(&instance->_bindParameters); TRI_DestroyAssociativePointer(&instance->_bindParameters); TRI_FreeNodeVectorQuery(&instance->_memory._nodes); TRI_FreeStringVectorQuery(&instance->_memory._strings); QLAstQueryFreeCollections(&instance->_query._from._collections); if (instance->_query._where._context) { TRI_FreeExecutionContext(instance->_query._where._context); } FreeJoinsQueryInstance(instance); if (instance->_locks) { // might be a NULL pointer if the locks where handed over to the result cursor TRI_FreeLocksQueryInstance(instance->_template->_vocbase, instance->_locks); } TRI_Free(instance); LOG_DEBUG("destroyed query instance"); } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a query instance with bind parameters (may be empty) //////////////////////////////////////////////////////////////////////////////// TRI_query_instance_t* TRI_CreateQueryInstance (const TRI_query_template_t* const template_, const TRI_json_t* parameters) { TRI_query_instance_t* instance; assert(template_); instance = (TRI_query_instance_t*) TRI_Allocate(sizeof(TRI_query_instance_t)); if (!instance) { return NULL; } // init lock struct instance->_locks = TRI_InitLocksQueryInstance(); if (!instance->_locks) { TRI_Free(instance); return NULL; } // init values instance->_template = (TRI_query_template_t*) template_; instance->_wasKilled = false; instance->_doAbort = false; TRI_InitQueryError(&instance->_error); // init vectors needed for book-keeping memory TRI_InitVectorPointer(&instance->_memory._nodes); TRI_InitVectorPointer(&instance->_memory._strings); TRI_InitVectorPointer(&instance->_join); TRI_InitAssociativePointer(&instance->_bindParameters, TRI_HashStringKeyAssociativePointer, HashBindParameter, EqualBindParameter, 0); TRI_InitAssociativePointer(&instance->_query._from._collections, TRI_HashStringKeyAssociativePointer, QLHashCollectionElement, QLEqualCollectionKeyElement, 0); // set up bind parameters if (parameters) { if (!AddBindParameterValues(instance, parameters)) { return instance; } } // validate bind parameters if (!ValidateBindParameters(instance)) { return instance; } if (!InitPartsQueryInstance(instance)) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return instance; } if (!InitJoinsQueryInstance(instance)) { return instance; } if (!TRI_LockCollectionsQueryInstance(instance->_template->_vocbase, instance, instance->_locks)) { return instance; } LOG_DEBUG("created query instance"); return instance; } //////////////////////////////////////////////////////////////////////////////// /// @brief Kill a query instance //////////////////////////////////////////////////////////////////////////////// void TRI_KillQueryInstance (TRI_query_instance_t* const instance) { assert(instance); instance->_wasKilled = true; TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_QUERY_KILLED, NULL); } //////////////////////////////////////////////////////////////////////////////// /// @brief Register an error during query execution //////////////////////////////////////////////////////////////////////////////// bool TRI_RegisterErrorQueryInstance (TRI_query_instance_t* const instance, const int code, const char* data) { assert(instance); assert(code > 0); TRI_SetQueryError(&instance->_error, code, data); // set abort flag instance->_doAbort = true; return false; } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a node from a json data part //////////////////////////////////////////////////////////////////////////////// static TRI_query_node_t* CreateNodeFromJson (TRI_query_instance_t* const instance, const TRI_json_t* json) { TRI_query_node_t* node; TRI_query_node_t* nodePtr; char* stringValue; size_t i, n; assert(instance); node = TRI_CreateNodeQuery(&instance->_memory._nodes, TRI_QueryNodeValueUndefined); if (!node) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } switch (json->_type) { case TRI_JSON_UNUSED: break; case TRI_JSON_NULL: node->_type = TRI_QueryNodeValueNull; break; case TRI_JSON_BOOLEAN: node->_type = TRI_QueryNodeValueBool; node->_value._boolValue = json->_value._boolean; break; case TRI_JSON_NUMBER: node->_type = TRI_QueryNodeValueNumberDouble; node->_value._doubleValue = json->_value._number; break; case TRI_JSON_STRING: stringValue = TRI_DuplicateString(json->_value._string.data); if (stringValue) { node->_type = TRI_QueryNodeValueString; node->_value._stringValue = stringValue; TRI_RegisterStringQuery(&instance->_memory._strings, stringValue); } else { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } break; case TRI_JSON_ARRAY: node->_type = TRI_QueryNodeValueDocument; // create a linked list of sub nodes n = json->_value._objects._length; if (n > 0) { TRI_query_node_t* container; container = TRI_CreateNodeQuery(&instance->_memory._nodes, TRI_QueryNodeContainerList); if (!container) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } node->_rhs = container; nodePtr = container; for (i = 0; i < n; i += 2) { TRI_query_node_t* namedValueNode; namedValueNode = TRI_CreateNodeQuery(&instance->_memory._nodes, TRI_QueryNodeValueNamedValue); if (!namedValueNode) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } namedValueNode->_lhs = CreateNodeFromJson( instance, (TRI_json_t*) TRI_AtVector(&json->_value._objects, i) ); namedValueNode->_rhs = CreateNodeFromJson( instance, (TRI_json_t*) TRI_AtVector(&json->_value._objects, i + 1) ); if (!namedValueNode->_lhs || !namedValueNode->_rhs) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } nodePtr->_next = namedValueNode; nodePtr = namedValueNode; } } break; case TRI_JSON_LIST: node->_type = TRI_QueryNodeValueArray; // create a linked list of sub nodes n = json->_value._objects._length; if (n > 0) { TRI_query_node_t* container; container = TRI_CreateNodeQuery(&instance->_memory._nodes, TRI_QueryNodeContainerList); if (!container) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } node->_rhs = container; nodePtr = container; for (i = 0; i < n; i++) { TRI_query_node_t* subNode; subNode = CreateNodeFromJson( instance, (TRI_json_t*) TRI_AtVector(&json->_value._objects, i) ); if (!subNode) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } nodePtr->_next = subNode; nodePtr = subNode; } } break; } return node; } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a node from a bind parameter //////////////////////////////////////////////////////////////////////////////// static TRI_query_node_t* CreateNodeFromBindParameter (TRI_query_instance_t* const instance, const TRI_query_node_t* const node) { TRI_bind_parameter_t* parameter; // node is a bind parameter, now insert bind parameter values assert(node->_value._stringValue); parameter = (TRI_bind_parameter_t*) TRI_LookupByKeyAssociativePointer( &instance->_bindParameters, node->_value._stringValue ); assert(parameter); assert(parameter->_data); return CreateNodeFromJson(instance, parameter->_data); } //////////////////////////////////////////////////////////////////////////////// /// @brief Copy a part of the query's AST and insert bind parameter values on /// the fly //////////////////////////////////////////////////////////////////////////////// TRI_query_node_t* TRI_CopyQueryPartQueryInstance (TRI_query_instance_t* const instance, const TRI_query_node_t* const node) { TRI_query_node_t* copy; TRI_query_node_t* next; if (node->_type == TRI_QueryNodeValueParameterNamed) { return CreateNodeFromBindParameter(instance, node); } copy = TRI_CreateNodeQuery(&instance->_memory._nodes, node->_type); if (!copy) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } copy->_value = node->_value; next = node->_next; while (next) { copy->_next = TRI_CopyQueryPartQueryInstance(instance, next); if (!copy->_next) { TRI_RegisterErrorQueryInstance(instance, TRI_ERROR_OUT_OF_MEMORY, NULL); return NULL; } next = next->_next; } if (node->_lhs) { copy->_lhs = TRI_CopyQueryPartQueryInstance(instance, node->_lhs); } if (node->_rhs) { copy->_rhs = TRI_CopyQueryPartQueryInstance(instance, node->_rhs); } return copy; } // ----------------------------------------------------------------------------- // --SECTION-- errors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief Register an error //////////////////////////////////////////////////////////////////////////////// void TRI_SetQueryError (TRI_query_error_t* const error, const int code, const char* data) { assert(code > 0); if (TRI_GetCodeQueryError(error) == 0) { // do not overwrite previous error TRI_set_errno(code); error->_code = code; error->_message = (char*) TRI_last_error(); if (data) { error->_data = TRI_DuplicateString(data); } } } //////////////////////////////////////////////////////////////////////////////// /// @brief Get the error code registered last //////////////////////////////////////////////////////////////////////////////// int TRI_GetCodeQueryError (const TRI_query_error_t* const error) { assert(error); return error->_code; } //////////////////////////////////////////////////////////////////////////////// /// @brief Get the error string registered last //////////////////////////////////////////////////////////////////////////////// char* TRI_GetStringQueryError (const TRI_query_error_t* const error) { char* message = NULL; char buffer[1024]; int code; assert(error); code = TRI_GetCodeQueryError(error); if (!code) { return NULL; } message = error->_message; if (!message) { return NULL; } if (error->_data && (NULL != strstr(message, "%s"))) { snprintf(buffer, sizeof(buffer), message, error->_data); return TRI_DuplicateString((const char*) &buffer); } return TRI_DuplicateString(message); } //////////////////////////////////////////////////////////////////////////////// /// @brief initialize an error structure //////////////////////////////////////////////////////////////////////////////// void TRI_InitQueryError (TRI_query_error_t* const error) { assert(error); error->_code = 0; error->_data = NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief free an error structure //////////////////////////////////////////////////////////////////////////////// void TRI_FreeQueryError (TRI_query_error_t* const error) { assert(error); if (error->_data) { TRI_Free(error->_data); } } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: