//////////////////////////////////////////////////////////////////////////////// /// @brief Aql, query context /// /// @file /// /// DISCLAIMER /// /// Copyright 2014 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 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 ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "Aql/Query.h" #include "Aql/ExecutionBlock.h" #include "Aql/Parser.h" #include "Aql/PlanGenerator.h" #include "Aql/V8Executor.h" #include "BasicsC/json.h" #include "BasicsC/tri-strings.h" #include "Utils/Exception.h" #include "VocBase/vocbase.h" using namespace triagens::aql; // ----------------------------------------------------------------------------- // --SECTION-- constructors / destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief creates a query //////////////////////////////////////////////////////////////////////////////// Query::Query (TRI_vocbase_t* vocbase, char const* queryString, size_t queryLength, TRI_json_t* bindParameters) : _vocbase(vocbase), _executor(nullptr), _queryString(queryString), _queryLength(queryLength), _type(AQL_QUERY_READ), _bindParameters(bindParameters), _strings() { TRI_ASSERT(_vocbase != nullptr); _strings.reserve(32); } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a query //////////////////////////////////////////////////////////////////////////////// Query::~Query () { if (_executor != nullptr) { delete _executor; _executor = nullptr; } // free strings for (auto it = _strings.begin(); it != _strings.end(); ++it) { TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, const_cast(*it)); } } // ----------------------------------------------------------------------------- // --SECTION-- public methods // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @brief extract a region from the query //////////////////////////////////////////////////////////////////////////////// std::string Query::extractRegion (int line, int column) const { // note: line numbers reported by bison/flex start at 1, columns start at 0 int currentLine = 1; int currentColumn = 0; char c; char const* p = _queryString; while ((c = *p)) { if (currentLine > line || (currentLine >= line && currentColumn >= column)) { break; } if (c == '\n') { ++p; ++currentLine; currentColumn = 0; } else if (c == '\r') { ++p; ++currentLine; currentColumn = 0; // eat a following newline if (*p == '\n') { ++p; } } else { ++currentColumn; ++p; } } // p is pointing at the position in the query the parse error occurred at TRI_ASSERT(p >= _queryString); size_t offset = static_cast(p - _queryString); static int const SNIPPET_LENGTH = 32; static char const* SNIPPET_SUFFIX = "..."; if (_queryLength < offset + SNIPPET_LENGTH) { // return a copy of the region return std::string(_queryString + offset, _queryLength - offset); } // copy query part std::string result(_queryString + offset, SNIPPET_LENGTH); result.append(SNIPPET_SUFFIX); return result; } //////////////////////////////////////////////////////////////////////////////// /// @brief register an error //////////////////////////////////////////////////////////////////////////////// void Query::registerError (int code, char const* details) { TRI_ASSERT(code != TRI_ERROR_NO_ERROR); if (details == nullptr) { THROW_ARANGO_EXCEPTION(code); } else { THROW_ARANGO_EXCEPTION_STRING(code, details); } } //////////////////////////////////////////////////////////////////////////////// /// @brief execute an AQL query - TODO: implement and determine return type //////////////////////////////////////////////////////////////////////////////// QueryResult Query::execute () { try { Parser parser(this); parser.parse(); parser.ast()->injectBindParameters(_bindParameters); parser.ast()->optimize(); PlanGenerator generator; auto plan = generator.fromAst(parser.ast()); try { auto exec = ExecutionBlock::instanciatePlan(plan); try { exec->staticAnalysis(); exec->initialize(); exec->execute(); AqlItemBlock* value; while (nullptr != (value = exec->getOne())) { std::cout << value->getValue(0, 0)->toString() << std::endl; delete value; } exec->shutdown(); delete exec; } catch (...) { delete exec; delete plan; // TODO: convert exception code return QueryResult(TRI_ERROR_INTERNAL); } } catch (triagens::arango::Exception const& ex) { delete plan; return QueryResult(ex.code(), ex.message()); } catch (...) { delete plan; // TODO: convert exception code return QueryResult(TRI_ERROR_INTERNAL); } delete plan; QueryResult result(TRI_ERROR_NO_ERROR); // result.json = parser.ast()->toJson(TRI_UNKNOWN_MEM_ZONE); return result; } catch (triagens::arango::Exception const& ex) { return QueryResult(ex.code(), ex.message()); } catch (...) { return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); } } //////////////////////////////////////////////////////////////////////////////// /// @brief parse an AQL query //////////////////////////////////////////////////////////////////////////////// QueryResult Query::parse () { try { Parser parser(this); return parser.parse(); } catch (triagens::arango::Exception const& ex) { return QueryResult(ex.code(), ex.message()); } catch (...) { return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); } } //////////////////////////////////////////////////////////////////////////////// /// @brief explain an AQL query - TODO: implement and determine return type //////////////////////////////////////////////////////////////////////////////// void Query::explain () { } //////////////////////////////////////////////////////////////////////////////// /// @brief get v8 executor //////////////////////////////////////////////////////////////////////////////// V8Executor* Query::executor () { if (_executor == nullptr) { // the executor is a singleton per query _executor = new V8Executor; } TRI_ASSERT(_executor != nullptr); return _executor; } //////////////////////////////////////////////////////////////////////////////// /// @brief register a string /// the string is freed when the query is destroyed //////////////////////////////////////////////////////////////////////////////// char* Query::registerString (char const* p, size_t length, bool mustUnescape) { if (p == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } if (length == 0) { static char const* empty = ""; // optimisation for the empty string return const_cast(empty); } char* copy = nullptr; if (mustUnescape) { size_t outLength; copy = TRI_UnescapeUtf8StringZ(TRI_UNKNOWN_MEM_ZONE, p, length, &outLength); } else { copy = TRI_DuplicateString2Z(TRI_UNKNOWN_MEM_ZONE, p, length); } if (copy == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } try { _strings.push_back(copy); } catch (...) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } return copy; } // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: