1
0
Fork 0
arangodb/QL/ParserWrapper.cpp

414 lines
13 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief QL parser wrapper and utility classes
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-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 "ParserWrapper.h"
using namespace std;
using namespace triagens::avocado;
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup QL
/// @{
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- class ParseError
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief Create a new parse error instance
////////////////////////////////////////////////////////////////////////////////
ParseError::ParseError (const string& message, const size_t line, const size_t column) :
_message(message), _line(line), _column(column) {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Destroy the instance
////////////////////////////////////////////////////////////////////////////////
ParseError::~ParseError () {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the parse error as a formatted string
////////////////////////////////////////////////////////////////////////////////
string ParseError::getDescription () const {
ostringstream errorMessage;
errorMessage << "Parse error at " << _line << "," << _column << ": " << _message;
return errorMessage.str();
}
// -----------------------------------------------------------------------------
// --SECTION-- class ParserWrapper
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief Create a new instance
////////////////////////////////////////////////////////////////////////////////
ParserWrapper::ParserWrapper (const char *query) :
_query(query), _parseError(0), _isParsed(false) {
_context = (QL_parser_context_t*) TRI_Allocate(sizeof(QL_parser_context_t));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Destroy the instance and free all associated memory
////////////////////////////////////////////////////////////////////////////////
ParserWrapper::~ParserWrapper () {
if (_context != 0) {
QLParseFree(_context);
}
if (_parseError) {
delete _parseError;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Parse the query passed
////////////////////////////////////////////////////////////////////////////////
bool ParserWrapper::parse () {
if (_context == 0) {
return false;
}
if (_query == 0) {
return false;
}
if (_isParsed) {
return false;
}
_isParsed = true;
if (!QLParseInit(_context, _query)) {
return false;
}
if (QLparse(_context)) {
// parse error
QL_error_state_t *errorState = &_context->_lexState._errorState;
_parseError= new ParseError(errorState->_message, errorState->_line, errorState->_column);
return false;
}
if (!QLParseValidate(_context, _context->_query->_select._base)) {
QL_error_state_t *errorState = &_context->_lexState._errorState;
_parseError= new ParseError(errorState->_message, errorState->_line, errorState->_column);
return false;
}
if (!QLParseValidate(_context, _context->_query->_where._base)) {
QL_error_state_t *errorState = &_context->_lexState._errorState;
_parseError= new ParseError(errorState->_message, errorState->_line, errorState->_column);
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Get a parse error that may have occurred
////////////////////////////////////////////////////////////////////////////////
ParseError* ParserWrapper::getParseError () {
return _parseError;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the type of the query
////////////////////////////////////////////////////////////////////////////////
QL_ast_query_type_e ParserWrapper::getQueryType () {
if (!_isParsed) {
return QLQueryTypeUndefined;
}
return _context->_query->_type;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Create a select clause
////////////////////////////////////////////////////////////////////////////////
TRI_qry_select_t* ParserWrapper::getSelect () {
TRI_qry_select_t* select = 0;
if (!_isParsed) {
return 0;
}
QLOptimizeExpression(_context->_query->_select._base);
QL_ast_query_select_type_e selectType = QLOptimizeGetSelectType(_context->_query);
if (selectType == QLQuerySelectTypeSimple) {
select = TRI_CreateQuerySelectDocument();
}
else if (selectType == QLQuerySelectTypeEvaluated) {
QL_javascript_conversion_t *selectJs = QLJavascripterInit();
if (selectJs != 0) {
TRI_AppendStringStringBuffer(selectJs->_buffer, "(function($) { return ");
QLJavascripterConvert(selectJs, _context->_query->_select._base);
TRI_AppendStringStringBuffer(selectJs->_buffer, " })");
select = TRI_CreateQuerySelectGeneral(selectJs->_buffer->_buffer);
// TODO: REMOVE ME
// std::cout << "SELECT: " << selectJs->_buffer->_buffer << "\n";
QLJavascripterFree(selectJs);
}
}
return select;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the alias of the primary collection
////////////////////////////////////////////////////////////////////////////////
char* ParserWrapper::getPrimaryAlias () {
if (_isParsed) {
return QLAstQueryGetPrimaryAlias(_context->_query);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the name of the primary collection
////////////////////////////////////////////////////////////////////////////////
char* ParserWrapper::getPrimaryName () {
if (_isParsed) {
return QLAstQueryGetPrimaryName(_context->_query);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Create joins
////////////////////////////////////////////////////////////////////////////////
TRI_select_join_t* ParserWrapper::getJoins () {
TRI_select_join_t* join = 0;
char *collectionName;
char *collectionAlias;
if (!_isParsed ) {
return NULL;
}
join = TRI_CreateSelectJoin();
if (!join) {
return NULL;
}
QL_ast_node_t *node = (QL_ast_node_t *) _context->_query->_from._base;
node = (QL_ast_node_t *) node->_next;
assert(node != 0);
// primary table
QL_ast_node_t *lhs = (QL_ast_node_t *) node->_lhs;
QL_ast_node_t *rhs = (QL_ast_node_t *) node->_rhs;
collectionName = lhs->_value._stringValue;
collectionAlias = rhs->_value._stringValue;
TRI_AddPartSelectJoin(join, JOIN_TYPE_PRIMARY, NULL, collectionName, collectionAlias);
while (node->_next) {
node = (QL_ast_node_t *) node->_next;
QL_ast_node_t *ref = (QL_ast_node_t *) node->_lhs;
QL_ast_node_t *condition = (QL_ast_node_t *) node->_rhs;
QLOptimizeExpression(condition);
QL_ast_query_where_type_e conditionType = QLOptimizeGetWhereType(condition);
TRI_qry_where_t* joinWhere = 0;
if (conditionType == QLQueryWhereTypeAlwaysTrue) {
// join condition is always true
joinWhere = TRI_CreateQueryWhereBoolean(true);
}
else if (conditionType == QLQueryWhereTypeAlwaysFalse) {
// join condition is always false
joinWhere = TRI_CreateQueryWhereBoolean(false);
}
else {
// where condition must be evaluated for each result
QL_javascript_conversion_t *conditionJs = QLJavascripterInit();
if (conditionJs != 0) {
TRI_AppendStringStringBuffer(conditionJs->_buffer, "(function($) { return (");
QLJavascripterConvert(conditionJs, condition);
TRI_AppendStringStringBuffer(conditionJs->_buffer, "); })");
joinWhere = TRI_CreateQueryWhereGeneral(conditionJs->_buffer->_buffer);
QLJavascripterFree(conditionJs);
}
}
collectionName = ((QL_ast_node_t *) (ref->_lhs))->_value._stringValue;
collectionAlias = ((QL_ast_node_t *) (ref->_rhs))->_value._stringValue;
if (node->_type == QLNodeJoinList) {
TRI_AddPartSelectJoin(join, JOIN_TYPE_LIST, joinWhere, collectionName, collectionAlias);
}
else if (node->_type == QLNodeJoinInner) {
TRI_AddPartSelectJoin(join, JOIN_TYPE_INNER, joinWhere, collectionName, collectionAlias);
}
else if (node->_type == QLNodeJoinLeft || node->_type == QLNodeJoinRight) {
TRI_AddPartSelectJoin(join, JOIN_TYPE_OUTER, joinWhere, collectionName, collectionAlias);
}
}
return join;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Create a where clause
////////////////////////////////////////////////////////////////////////////////
TRI_qry_where_t* ParserWrapper::getWhere () {
TRI_qry_where_t* where = 0;
if (!_isParsed) {
return NULL;
}
QLOptimizeExpression(_context->_query->_where._base);
// TODO: REMOVE ME
// QL_formatter_t f;
// f.indentLevel = 0;
// QLFormatterDump(_context->_query->_where._base, &f, 0);
_context->_query->_where._type = QLOptimizeGetWhereType(_context->_query->_where._base);
if (_context->_query->_where._type == QLQueryWhereTypeAlwaysTrue) {
// where condition is always true
where = TRI_CreateQueryWhereBoolean(true);
}
else if (_context->_query->_where._type == QLQueryWhereTypeAlwaysFalse) {
// where condition is always false
where = TRI_CreateQueryWhereBoolean(false);
}
else {
// where condition must be evaluated for each result
QL_javascript_conversion_t *whereJs = QLJavascripterInit();
if (whereJs != 0) {
TRI_AppendStringStringBuffer(whereJs->_buffer, "(function($) { return (");
QLJavascripterConvert(whereJs, _context->_query->_where._base);
TRI_AppendStringStringBuffer(whereJs->_buffer, "); })");
where = TRI_CreateQueryWhereGeneral(whereJs->_buffer->_buffer);
// TODO: REMOVE ME
// std::cout << "WHERE: " << whereJs->_buffer->_buffer << "\n";
QLJavascripterFree(whereJs);
}
}
return where;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Create an order clause
////////////////////////////////////////////////////////////////////////////////
TRI_qry_order_t* ParserWrapper::getOrder () {
TRI_qry_order_t* order = 0;
if (!_isParsed ) {
return NULL;
}
if (_context->_query->_order._base) {
QLOptimizeOrder(_context->_query->_order._base);
QL_javascript_conversion_t *orderJs = QLJavascripterInit();
if (orderJs != 0) {
TRI_AppendStringStringBuffer(orderJs->_buffer, "(function($) { return (");
QLJavascripterConvertOrder(orderJs, (QL_ast_node_t *) _context->_query->_order._base->_next);
TRI_AppendStringStringBuffer(orderJs->_buffer, "); })");
order = TRI_CreateQueryOrderGeneral(orderJs->_buffer->_buffer);
// TODO: REMOVE ME
// std::cout << "ORDER: " << orderJs->_buffer->_buffer << "\n";
QLJavascripterFree(orderJs);
}
}
return order;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the skip value
////////////////////////////////////////////////////////////////////////////////
TRI_voc_size_t ParserWrapper::getSkip () {
TRI_voc_size_t skip = 0;
if (_isParsed) {
if (_context->_query->_limit._isUsed) {
skip = (TRI_voc_size_t) _context->_query->_limit._offset;
}
else {
skip = TRI_QRY_NO_SKIP;
}
}
return skip;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief Get the limit value
////////////////////////////////////////////////////////////////////////////////
TRI_voc_ssize_t ParserWrapper::getLimit () {
TRI_voc_ssize_t limit = 0;
if (_isParsed) {
if (_context->_query->_limit._isUsed) {
limit = (TRI_voc_ssize_t) _context->_query->_limit._count;
}
else {
limit = TRI_QRY_NO_LIMIT;
}
}
return limit;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
// End: