1
0
Fork 0
arangodb/QL/ast-query.c

664 lines
22 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @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: