1
0
Fork 0
arangodb/VocBase/query-parse.c

476 lines
15 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @brief query parsing
///
/// @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-parse.h"
#include "VocBase/query-functions.h"
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup VocBase
/// @{
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief Validate the structs contained in a query template
////////////////////////////////////////////////////////////////////////////////
static bool ValidateQueryTemplate (TRI_query_template_t* const template_) {
size_t order = 0;
if (!TRI_ParseQueryValidateCollections(template_,
template_->_query->_select._base,
&QLAstQueryIsValidAlias,
&order)) {
// select expression invalid
return false;
}
if (!TRI_ParseQueryValidateCollections(template_,
template_->_query->_where._base,
&QLAstQueryIsValidAlias,
&order)) {
// where expression invalid
return false;
}
if (!TRI_ParseQueryValidateCollections(template_,
template_->_query->_order._base,
&QLAstQueryIsValidAlias,
&order)) {
// order expression invalid
return false;
}
order = 0;
if (!TRI_ParseQueryValidateCollections(template_,
template_->_query->_from._base,
&QLAstQueryIsValidAliasOrdered,
&order)) {
// from expression(s) invalid
return false;
}
if (!TRI_ParseQueryValidateFunctionCalls(template_,
template_->_query->_select._base)) {
return false;
}
if (!TRI_ParseQueryValidateFunctionCalls(template_,
template_->_query->_where._base)) {
return false;
}
if (!TRI_ParseQueryValidateFunctionCalls(template_,
template_->_query->_order._base)) {
return false;
}
if (!TRI_ParseQueryValidateFunctionCalls(template_,
template_->_query->_from._base)) {
return false;
}
return true;
}
// -----------------------------------------------------------------------------
// --SECTION-- parser
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief Create parser for the query
////////////////////////////////////////////////////////////////////////////////
static TRI_query_parser_t* InitParserQueryTemplate (TRI_query_template_t* const template_) {
TRI_query_parser_t* parser;
assert(template_);
assert(template_->_queryString);
parser = (TRI_query_parser_t*) TRI_Allocate(sizeof(TRI_query_parser_t));
if (!parser) {
TRI_SetQueryError(&template_->_error, TRI_ERROR_OUT_OF_MEMORY, NULL);
return NULL;
}
// init query data
parser->_length = strlen(template_->_queryString);
parser->_buffer = (char*) template_->_queryString;
// init lexer/scanner
QLlex_init(&parser->_scanner);
QLset_extra(template_, parser->_scanner);
return parser;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Free the parser for the query
////////////////////////////////////////////////////////////////////////////////
static void FreeParserQueryTemplate (TRI_query_template_t* const template_) {
assert(template_);
assert(template_->_parser);
// free lexer/scanner
QLlex_destroy(template_->_parser->_scanner);
TRI_Free(template_->_parser);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Parse the query contained in a query template
////////////////////////////////////////////////////////////////////////////////
bool TRI_ParseQueryTemplate (TRI_query_template_t* const template_) {
assert(template_);
assert(template_->_queryString);
assert(template_->_query);
template_->_parser = InitParserQueryTemplate(template_);
if (!template_->_parser) {
TRI_SetQueryError(&template_->_error, TRI_ERROR_OUT_OF_MEMORY, NULL);
return false;
}
if (QLparse(template_)) {
FreeParserQueryTemplate(template_);
return false;
}
FreeParserQueryTemplate(template_);
if (template_->_query->_type == QUERY_TYPE_EMPTY) {
TRI_SetQueryError(&template_->_error,
TRI_ERROR_QUERY_EMPTY,
NULL);
return false;
}
if (!ValidateQueryTemplate(template_)) {
return false;
}
return TRI_InitQueryTemplate(template_);
}
// -----------------------------------------------------------------------------
// --SECTION-- parser helper functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief copies a string and keeps track of its memory location in a vector
////////////////////////////////////////////////////////////////////////////////
char* TRI_ParseQueryAllocString (TRI_query_template_t* const template_,
const char* string) {
// do string duplication
char* copy = TRI_DuplicateString(string);
// store pointer to copy
return TRI_ParseQueryRegisterString(template_, copy);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief copies a string part and keeps track of its memory location in a vector
////////////////////////////////////////////////////////////////////////////////
char* TRI_ParseQueryAllocString2 (TRI_query_template_t* const template_,
const char* string,
const size_t length) {
// do string part duplication
char* copy = TRI_DuplicateString2(string, length);
// store pointer to copy and return it
return TRI_ParseQueryRegisterString(template_, copy);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief keep track of an allocated string
////////////////////////////////////////////////////////////////////////////////
char* TRI_ParseQueryRegisterString (TRI_query_template_t* const template_,
const char* string) {
TRI_PushBackVectorPointer(&template_->_memory._strings, (char*) string);
return (char*) string;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free a string
////////////////////////////////////////////////////////////////////////////////
void TRI_ParseQueryFreeString (char* string) {
if (string) {
TRI_Free(string);
string = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new node for the ast
////////////////////////////////////////////////////////////////////////////////
TRI_query_node_t* TRI_ParseQueryCreateNode (TRI_query_template_t* const template_,
const TRI_query_node_type_e type) {
return TRI_CreateNodeQuery(&template_->_memory._nodes, type);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief open a new context layer for the parser
////////////////////////////////////////////////////////////////////////////////
void TRI_ParseQueryContextPush (TRI_query_template_t* const template_,
TRI_query_node_t* element) {
TRI_PushBackVectorPointer(&template_->_memory._listHeads, element);
TRI_PushBackVectorPointer(&template_->_memory._listTails, element);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief close the current context layer of the parser and return it
////////////////////////////////////////////////////////////////////////////////
TRI_query_node_t* TRI_ParseQueryContextPop (TRI_query_template_t* const template_) {
TRI_query_node_t* head;
size_t i;
i = template_->_memory._listHeads._length;
if (i > 0) {
TRI_RemoveVectorPointer(&template_->_memory._listTails, i - 1);
head = TRI_RemoveVectorPointer(&template_->_memory._listHeads, i - 1);
return head;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add an element to the current parsing context
////////////////////////////////////////////////////////////////////////////////
void TRI_ParseQueryContextAddElement (TRI_query_template_t* const template_,
TRI_query_node_t* element) {
TRI_query_node_t* last;
size_t i;
i = template_->_memory._listTails._length;
if (i > 0) {
last = *(template_->_memory._listTails._buffer + i -1);
if (last) {
last->_next = element;
*(template_->_memory._listTails._buffer + i -1) = element;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief pop the current parse context from the stack into the rhs element
////////////////////////////////////////////////////////////////////////////////
void TRI_ParseQueryPopIntoRhs (TRI_query_node_t* node,
TRI_query_template_t* const template_) {
TRI_query_node_t* popped;
popped = TRI_ParseQueryContextPop(template_);
if (node) {
node->_rhs = popped;
}
}
// -----------------------------------------------------------------------------
// --SECTION-- validation
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief Validate a collection name
////////////////////////////////////////////////////////////////////////////////
bool TRI_ParseQueryValidateCollectionName (const char* name) {
if (TRI_IsAllowedCollectionName(name) != 0) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Validate a collection alias
////////////////////////////////////////////////////////////////////////////////
bool TRI_ParseQueryValidateCollectionAlias (const char* name) {
const char* p = name;
char c;
size_t totalLength = 0;
size_t charLength = 0;
c = *p;
// must start with one these chars
if (!(c >= 'A' && c <= 'Z') &&
!(c >= 'a' && c <= 'z') &&
!(c == '_')) {
return false;
}
while ('\0' != (c = *p++)) {
if (!(c >= 'A' && c <= 'Z') &&
!(c >= 'a' && c <= 'z') &&
!(c >= '0' && c <='9') &&
!(c == '_')) {
return false;
}
if (!(c >= '0' && c <='9') &&
!(c == '_')) {
// must include at least one letter
charLength++;
}
totalLength++;
}
// if no letter is contained, the alias is invalid
if (charLength == 0) {
return false;
}
return ((totalLength > 0) && (totalLength <= TRI_QUERY_ALIAS_MAX_LENGTH));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Validate the function names used in a query part
////////////////////////////////////////////////////////////////////////////////
bool TRI_ParseQueryValidateFunctionCalls (TRI_query_template_t* const template_,
const TRI_query_node_t* const node) {
TRI_query_node_t* lhs;
TRI_query_node_t* rhs;
TRI_query_node_t* next;
if (!node) {
return true;
}
if (node->_type == TRI_QueryNodeContainerList) {
next = node->_next;
while (next) {
if (!TRI_ParseQueryValidateFunctionCalls(template_, next)) {
return false;
}
next = next->_next;
}
}
if (node->_type == TRI_QueryNodeControlFunctionCall) {
//char* funcName;
//assert(node->_lhs); // function name
//funcName = node->_lhs->_value._stringValue;
// TODO FIXME: validate arguments
}
lhs = node->_lhs;
if (lhs && !TRI_ParseQueryValidateFunctionCalls(template_, lhs)) {
return false;
}
rhs = node->_rhs;
if (rhs && !TRI_ParseQueryValidateFunctionCalls(template_, rhs)) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Validate the collections used in a query part
////////////////////////////////////////////////////////////////////////////////
bool TRI_ParseQueryValidateCollections (TRI_query_template_t* const template_,
const TRI_query_node_t* const node,
QL_parser_validate_func validateFunc,
size_t* order) {
TRI_query_node_t* lhs;
TRI_query_node_t* rhs;
TRI_query_node_t* next;
if (!node) {
return true;
}
if (node->_type == TRI_QueryNodeContainerList) {
next = node->_next;
while (next) {
if (!TRI_ParseQueryValidateCollections(template_, next, validateFunc, order)) {
return false;
}
next = next->_next;
}
}
if (node->_type == TRI_QueryNodeReferenceCollection) {
++(*order);
}
if (node->_type == TRI_QueryNodeReferenceCollectionAlias) {
if (!validateFunc(template_->_query, node->_value._stringValue, *order)) {
TRI_SetQueryError(&template_->_error,
TRI_ERROR_QUERY_COLLECTION_ALIAS_UNDECLARED,
node->_value._stringValue);
return false;
}
}
lhs = node->_lhs;
if (lhs) {
if (!TRI_ParseQueryValidateCollections(template_, lhs, validateFunc, order)) {
return false;
}
}
rhs = node->_rhs;
if (rhs) {
if (!TRI_ParseQueryValidateCollections(template_, rhs, validateFunc, order)) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End: