//////////////////////////////////////////////////////////////////////////////// /// @brief AST query declarations /// /// @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 "QL/ast-query.h" // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup QL /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief Hash function used to hash geo restrictions //////////////////////////////////////////////////////////////////////////////// static uint64_t HashRestrictionElement (TRI_associative_pointer_t* array, void const* element) { QL_ast_query_geo_restriction_t* restriction = (QL_ast_query_geo_restriction_t*) element; return TRI_FnvHashString(restriction->_alias); } //////////////////////////////////////////////////////////////////////////////// /// @brief Comparison function used to determine hash key equality //////////////////////////////////////////////////////////////////////////////// static bool EqualRestrictionKeyElement (TRI_associative_pointer_t* array, void const* key, void const* element) { char const* k = (char const*) key; QL_ast_query_geo_restriction_t* restriction = (QL_ast_query_geo_restriction_t*) element; return TRI_EqualString(k, restriction->_alias); } //////////////////////////////////////////////////////////////////////////////// /// @brief free geo restrictions previously registered //////////////////////////////////////////////////////////////////////////////// static void QLAstQueryFreeGeoRestrictions (TRI_associative_pointer_t* const array) { QL_ast_query_geo_restriction_t* restriction; size_t i; // destroy all elements in restrictions array for (i = 0; i < array->_nrAlloc; i++) { restriction = (QL_ast_query_geo_restriction_t*) array->_table[i]; if (restriction) { QLAstQueryFreeRestriction(restriction); } } // destroy restrictions array TRI_DestroyAssociativePointer(array); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup QL /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief Hash function used to hash elements in the collection //////////////////////////////////////////////////////////////////////////////// uint64_t QLHashCollectionElement (TRI_associative_pointer_t* array, void const* element) { QL_ast_query_collection_t* collection = (QL_ast_query_collection_t*) element; return TRI_FnvHashString(collection->_alias); } //////////////////////////////////////////////////////////////////////////////// /// @brief Comparison function used to determine hash key equality //////////////////////////////////////////////////////////////////////////////// bool QLEqualCollectionKeyElement (TRI_associative_pointer_t* array, void const* key, void const* element) { char const* k = (char const*) key; QL_ast_query_collection_t* collection = (QL_ast_query_collection_t*) element; return TRI_EqualString(k, collection->_alias); } //////////////////////////////////////////////////////////////////////////////// /// @brief free collections previously registered //////////////////////////////////////////////////////////////////////////////// void QLAstQueryFreeCollections (TRI_associative_pointer_t* const array) { QL_ast_query_collection_t* collection; size_t i; // destroy all elements in collection array for (i = 0; i < array->_nrAlloc; i++) { collection = (QL_ast_query_collection_t*) array->_table[i]; if (collection) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, collection); } } // destroy collection array itself TRI_DestroyAssociativePointer(array); } //////////////////////////////////////////////////////////////////////////////// /// @brief Initialize data structures for a query //////////////////////////////////////////////////////////////////////////////// void QLAstQueryInit (QL_ast_query_t* const query) { query->_type = QUERY_TYPE_UNDEFINED; query->_select._base = NULL; query->_select._functionCode = NULL; query->_select._usesBindParameters = false; query->_from._base = NULL; query->_where._base = NULL; query->_where._functionCode = NULL; query->_where._usesBindParameters = false; query->_order._base = NULL; query->_order._functionCode = NULL; query->_order._usesBindParameters = false; query->_limit._isUsed = false; query->_isEmpty = false; TRI_InitAssociativePointer(&query->_from._collections, TRI_UNKNOWN_MEM_ZONE, TRI_HashStringKeyAssociativePointer, QLHashCollectionElement, QLEqualCollectionKeyElement, 0); TRI_InitAssociativePointer(&query->_geo._restrictions, TRI_UNKNOWN_MEM_ZONE, TRI_HashStringKeyAssociativePointer, HashRestrictionElement, EqualRestrictionKeyElement, 0); } //////////////////////////////////////////////////////////////////////////////// /// @brief De-allocate data structures for a query //////////////////////////////////////////////////////////////////////////////// void QLAstQueryFree (QL_ast_query_t* const query) { // free collections data QLAstQueryFreeCollections(&query->_from._collections); // free geo restrictions data QLAstQueryFreeGeoRestrictions(&query->_geo._restrictions); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the total ref count for a collection //////////////////////////////////////////////////////////////////////////////// size_t QLAstQueryGetTotalRefCount (QL_ast_query_t* query, const char* alias) { QL_ast_query_collection_t* collection; assert(query); assert(alias); collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias); assert(collection); return (collection->_refCount._select + collection->_refCount._where + collection->_refCount._order + collection->_refCount._join); } //////////////////////////////////////////////////////////////////////////////// /// @brief get the ref count for a collection //////////////////////////////////////////////////////////////////////////////// size_t QLAstQueryGetRefCount (QL_ast_query_t* query, const char* alias, const QL_ast_query_ref_type_e type) { QL_ast_query_collection_t* collection; assert(query); assert(alias); collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias); assert(collection); switch (type) { case REF_TYPE_SELECT: return collection->_refCount._select; case REF_TYPE_WHERE: return collection->_refCount._where; case REF_TYPE_ORDER: return collection->_refCount._order; case REF_TYPE_JOIN: return collection->_refCount._join; } assert(false); } //////////////////////////////////////////////////////////////////////////////// /// @brief Increment ref count for a collection //////////////////////////////////////////////////////////////////////////////// void QLAstQueryAddRefCount (QL_ast_query_t* query, const char* alias, const QL_ast_query_ref_type_e type) { QL_ast_query_collection_t* collection; assert(query); assert(alias); collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias); assert(collection); switch (type) { case REF_TYPE_SELECT: ++collection->_refCount._select; break; case REF_TYPE_WHERE: ++collection->_refCount._where; break; case REF_TYPE_ORDER: ++collection->_refCount._order; break; case REF_TYPE_JOIN: ++collection->_refCount._join; break; } } //////////////////////////////////////////////////////////////////////////////// /// @brief Check if a collection was defined in a query //////////////////////////////////////////////////////////////////////////////// bool QLAstQueryIsValidAlias (QL_ast_query_t* query, const char* alias, const size_t order) { if (0 != TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias)) { return true; } return (0 != TRI_LookupByKeyAssociativePointer(&query->_geo._restrictions, alias)); } //////////////////////////////////////////////////////////////////////////////// /// @brief Check if a collection was defined in a query, taking order of /// declaration into account //////////////////////////////////////////////////////////////////////////////// bool QLAstQueryIsValidAliasOrdered (QL_ast_query_t* query, const char* alias, const size_t order) { QL_ast_query_collection_t* collection; collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias); if (!collection) { return false; } return (collection->_declarationOrder <= order); } //////////////////////////////////////////////////////////////////////////////// /// @brief Return the collection name for its alias //////////////////////////////////////////////////////////////////////////////// char* QLAstQueryGetCollectionNameForAlias (QL_ast_query_t* query, const char* alias) { QL_ast_query_collection_t* collection; collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias); if (!collection) { return NULL; } return collection->_name; } //////////////////////////////////////////////////////////////////////////////// /// @brief Add a collection to the query //////////////////////////////////////////////////////////////////////////////// bool QLAstQueryAddCollection (QL_ast_query_t* query, const char* name, const char* alias) { QL_ast_query_collection_t* collection; size_t num; if (TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias)) { // alias has already been declared for another collection - return an error return false; } if (TRI_LookupByKeyAssociativePointer(&query->_geo._restrictions, alias)) { // alias has already been declared for a geo restriction - return an error return false; } collection = (QL_ast_query_collection_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(QL_ast_query_collection_t), false); if (!collection) { return false; } num = query->_from._collections._nrUsed; collection->_name = (char*) name; collection->_alias = (char*) alias; // first collection added is always the primary collection collection->_isPrimary = (num == 0); collection->_refCount._select = 0; collection->_refCount._where = 0; collection->_refCount._order = 0; collection->_refCount._join = 0; collection->_declarationOrder = num + 1; collection->_geoRestriction = NULL; collection->_where._base = NULL; collection->_where._type = QLQueryWhereTypeUndefined; collection->_where._usesBindParameters = false; collection->_where._functionCode = NULL; TRI_InsertKeyAssociativePointer(&query->_from._collections, collection->_alias, collection, true); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief get the alias of the primary collection used in the query //////////////////////////////////////////////////////////////////////////////// char* QLAstQueryGetPrimaryAlias (const QL_ast_query_t* query) { size_t i; QL_ast_query_collection_t *collection; for (i = 0; i < query->_from._collections._nrAlloc; i++) { collection = query->_from._collections._table[i]; if (collection != 0 && collection->_isPrimary) { return collection->_alias; } } return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief get a geo restriction prototype //////////////////////////////////////////////////////////////////////////////// QL_ast_query_geo_restriction_t* QLAstQueryCreateRestriction (void) { QL_ast_query_geo_restriction_t* restriction; restriction = (QL_ast_query_geo_restriction_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(QL_ast_query_geo_restriction_t), false); if (!restriction) { return NULL; } restriction->_alias = NULL; restriction->_compareLat._collection = NULL; restriction->_compareLat._field = NULL; restriction->_compareLon._collection = NULL; restriction->_compareLon._field = NULL; return restriction; } //////////////////////////////////////////////////////////////////////////////// /// @brief clone a geo restriction //////////////////////////////////////////////////////////////////////////////// QL_ast_query_geo_restriction_t* QLAstQueryCloneRestriction (const QL_ast_query_geo_restriction_t* source) { QL_ast_query_geo_restriction_t* dest; if (!source) { return NULL; } dest = QLAstQueryCreateRestriction(); if (!dest) { return NULL; } dest->_type = source->_type; dest->_alias = TRI_DuplicateString(source->_alias); dest->_compareLat._collection = TRI_DuplicateString(source->_compareLat._collection); dest->_compareLat._field = TRI_DuplicateString(source->_compareLat._field); dest->_compareLon._collection = TRI_DuplicateString(source->_compareLon._collection); dest->_compareLon._field = TRI_DuplicateString(source->_compareLon._field); dest->_lat = source->_lat; dest->_lon = source->_lon; dest->_arg = source->_arg; return dest; } //////////////////////////////////////////////////////////////////////////////// /// @brief free a geo restriction //////////////////////////////////////////////////////////////////////////////// void QLAstQueryFreeRestriction (QL_ast_query_geo_restriction_t* restriction) { if (restriction->_compareLat._collection) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, restriction->_compareLat._collection); } if (restriction->_compareLat._field) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, restriction->_compareLat._field); } if (restriction->_compareLon._collection) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, restriction->_compareLon._collection); } if (restriction->_compareLon._field) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, restriction->_compareLon._field); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, restriction); } //////////////////////////////////////////////////////////////////////////////// /// @brief add a geo restriction //////////////////////////////////////////////////////////////////////////////// bool QLAstQueryAddGeoRestriction (QL_ast_query_t* query, const TRI_query_node_t* collectionNode, const TRI_query_node_t* restrictionNode) { TRI_query_node_t* valueNode; QL_ast_query_collection_t* collection; QL_ast_query_geo_restriction_t* restriction; TRI_string_buffer_t* fieldName; char* alias; char* collectionAlias; void* previous; if (!collectionNode || !restrictionNode) { return false; } alias = restrictionNode->_lhs->_value._stringValue; assert(alias); previous = (void*) TRI_LookupByKeyAssociativePointer(&query->_from._collections, alias); if (previous) { // alias is already used for another collection return false; } previous = (void*) TRI_LookupByKeyAssociativePointer(&query->_geo._restrictions, alias); if (previous) { // alias is already used for another geo restriction return false; } collectionAlias = ((TRI_query_node_t*) collectionNode->_rhs)->_value._stringValue; assert(collectionAlias); collection = (QL_ast_query_collection_t*) TRI_LookupByKeyAssociativePointer(&query->_from._collections, collectionAlias); if (!collection) { // collection is not present - should not happen return false; } assert(restrictionNode->_lhs); assert(restrictionNode->_rhs); restriction = QLAstQueryCreateRestriction(); if (!restriction) { return false; } restriction->_alias = alias; if (restrictionNode->_type == TRI_QueryNodeRestrictWithin) { restriction->_type = RESTRICT_WITHIN; } else { restriction->_type = RESTRICT_NEAR; } // compare field 1 valueNode = restrictionNode->_rhs->_lhs->_lhs; if (strcmp(valueNode->_lhs->_value._stringValue, collectionAlias) != 0) { // field is from other collection, not valid! QLAstQueryFreeRestriction(restriction); return false; } fieldName = QLAstQueryGetMemberNameString(valueNode, false); if (fieldName) { restriction->_compareLat._collection = TRI_DuplicateString(valueNode->_lhs->_value._stringValue); restriction->_compareLat._field = TRI_DuplicateString(fieldName->_buffer); TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, fieldName); } else { QLAstQueryFreeRestriction(restriction); return false; } // compare field 2 valueNode = restrictionNode->_rhs->_lhs->_rhs; if (strcmp(valueNode->_lhs->_value._stringValue, collectionAlias) != 0) { // field is from other collection, not valid! QLAstQueryFreeRestriction(restriction); return false; } fieldName = QLAstQueryGetMemberNameString(valueNode, false); if (fieldName) { restriction->_compareLon._collection = TRI_DuplicateString(valueNode->_lhs->_value._stringValue); restriction->_compareLon._field = TRI_DuplicateString(fieldName->_buffer); TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, fieldName); } else { QLAstQueryFreeRestriction(restriction); return false; } // lat value valueNode = restrictionNode->_rhs->_rhs->_lhs; restriction->_lat = valueNode->_value._doubleValue; // lon value valueNode = restrictionNode->_rhs->_rhs->_rhs; restriction->_lon = valueNode->_value._doubleValue; if (restrictionNode->_type == TRI_QueryNodeRestrictWithin) { restriction->_arg._radius = restrictionNode->_value._doubleValue; } else { restriction->_arg._numDocuments = (size_t) restrictionNode->_value._intValue; } TRI_InsertKeyAssociativePointer(&query->_geo._restrictions, alias, restriction, true); collection->_geoRestriction = restriction; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief Create a string from a member name //////////////////////////////////////////////////////////////////////////////// TRI_string_buffer_t* QLAstQueryGetMemberNameString (TRI_query_node_t* node, bool includeCollection) { TRI_query_node_t *lhs, *rhs; TRI_string_buffer_t* buffer; buffer = TRI_CreateStringBuffer(TRI_UNKNOWN_MEM_ZONE); if (!buffer) { return NULL; } if (includeCollection) { // add collection part lhs = node->_lhs; TRI_AppendStringStringBuffer(buffer, lhs->_value._stringValue); TRI_AppendCharStringBuffer(buffer, '.'); } rhs = node->_rhs; node = rhs->_next; while (node) { // add individual name parts TRI_AppendStringStringBuffer(buffer, node->_value._stringValue); node = node->_next; if (node) { TRI_AppendCharStringBuffer(buffer, '.'); } } return buffer; } //////////////////////////////////////////////////////////////////////////////// /// @brief Hash a member name for comparisons //////////////////////////////////////////////////////////////////////////////// uint64_t QLAstQueryGetMemberNameHash (TRI_query_node_t* node) { TRI_query_node_t *lhs, *rhs; uint64_t hashValue; lhs = node->_lhs; hashValue = TRI_FnvHashString(lhs->_value._stringValue); rhs = node->_rhs; node = rhs->_next; while (node) { hashValue ^= TRI_FnvHashString(node->_value._stringValue); node = node->_next; } return hashValue; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: