mirror of https://gitee.com/bigwinds/arangodb
476 lines
15 KiB
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:
|