1
0
Fork 0
arangodb/arangod/Aql/AqlFunctionFeature.cpp

373 lines
17 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2017 ArangoDB 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 Michael Hackstein
////////////////////////////////////////////////////////////////////////////////
#include "AqlFunctionFeature.h"
#include "Aql/AstNode.h"
#include "Cluster/ServerState.h"
#include "StorageEngine/EngineSelectorFeature.h"
#include "StorageEngine/StorageEngine.h"
using namespace arangodb;
using namespace arangodb::application_features;
using namespace arangodb::aql;
AqlFunctionFeature* AqlFunctionFeature::AQLFUNCTIONS = nullptr;
AqlFunctionFeature::AqlFunctionFeature(
application_features::ApplicationServer* server)
: application_features::ApplicationFeature(server, "AQLFunctions") {
setOptional(false);
startsAfter("EngineSelector");
startsAfter("Aql");
}
// This feature does not have any options
void AqlFunctionFeature::collectOptions(
std::shared_ptr<options::ProgramOptions>) {}
// This feature does not have any options
void AqlFunctionFeature::validateOptions(
std::shared_ptr<options::ProgramOptions>) {}
void AqlFunctionFeature::prepare() {
// set singleton
AQLFUNCTIONS = this;
Functions::init();
/// @brief Add all AQL functions to the FunctionDefintions map
addTypeCheckFunctions();
addTypeCastFunctions();
addStringFunctions();
addNumericFunctions();
addListFunctions();
addDocumentFunctions();
addGeoFunctions();
addGeometryConstructors();
addDateFunctions();
addMiscFunctions();
addStorageEngineFunctions();
add({"PREGEL_RESULT", ".", false, true,
true, &Functions::PregelResult});
}
void AqlFunctionFeature::unprepare() {
// Just unlink nothing more todo
AQLFUNCTIONS = nullptr;
}
/// @brief returns a reference to a built-in function
Function const* AqlFunctionFeature::getFunctionByName(std::string const& name) {
TRI_ASSERT(AQLFUNCTIONS != nullptr);
return AQLFUNCTIONS->byName(name);
}
void AqlFunctionFeature::add(Function const& func) {
TRI_ASSERT(_functionNames.find(func.name) == _functionNames.end());
// add function to the map
_functionNames.emplace(func.name, func);
}
void AqlFunctionFeature::addAlias(std::string const& alias, std::string const& original) {
auto it = _functionNames.find(original);
TRI_ASSERT(it != _functionNames.end());
// intentionally copy original function, as we want to give it another name
Function aliasFunction = (*it).second;
aliasFunction.name = alias;
add(aliasFunction);
}
void AqlFunctionFeature::toVelocyPack(VPackBuilder& builder) {
builder.openArray();
for (auto const& it : _functionNames) {
builder.openObject();
builder.add("name", VPackValue(it.second.name));
builder.add("arguments", VPackValue(it.second.arguments));
builder.add("implementations", VPackValue(VPackValueType::Array));
builder.add(VPackValue("js"));
if (it.second.implementation != nullptr) {
builder.add(VPackValue("cxx"));
}
builder.close(); // implementations
builder.add("deterministic", VPackValue(it.second.isDeterministic));
builder.add("cacheable", VPackValue(it.second.isCacheable()));
builder.add("canThrow", VPackValue(it.second.canThrow));
builder.close();
}
builder.close();
}
Function const* AqlFunctionFeature::byName(std::string const& name) {
auto it = _functionNames.find(name);
if (it == _functionNames.end()) {
THROW_ARANGO_EXCEPTION_PARAMS(TRI_ERROR_QUERY_FUNCTION_NAME_UNKNOWN,
name.c_str());
}
// return the address of the function
return &((*it).second);
}
// meanings of the symbols in the function arguments list
// ------------------------------------------------------
//
// . = argument of any type (except collection)
// c = collection name, will be converted into list with documents
// h = collection name, will be converted into string
// , = next argument
// | = separates mandatory from optional arguments
// + = unlimited number of optional arguments of any type
void AqlFunctionFeature::addTypeCheckFunctions() {
// type check functions
add({"IS_NULL", ".", true, false, true, &Functions::IsNull});
add({"IS_BOOL", ".", true, false, true, &Functions::IsBool});
add({"IS_NUMBER", ".", true, false, true, &Functions::IsNumber});
add({"IS_STRING", ".", true, false, true, &Functions::IsString});
add({"IS_ARRAY", ".", true, false, true, &Functions::IsArray});
// IS_LIST is an alias for IS_ARRAY
addAlias("IS_LIST", "IS_ARRAY");
add({"IS_OBJECT", ".", true, false, true, &Functions::IsObject});
// IS_DOCUMENT is an alias for IS_OBJECT
addAlias("IS_DOCUMENT", "IS_OBJECT");
add({"IS_DATESTRING", ".", true, false, true, &Functions::IsDatestring});
add({"IS_KEY", ".", true, false, true, &Functions::IsKey});
add({"TYPENAME", ".", true, false, true, &Functions::Typename});
}
void AqlFunctionFeature::addTypeCastFunctions() {
// type cast functions
add({"TO_NUMBER", ".", true, false, true, &Functions::ToNumber});
add({"TO_STRING", ".", true, false, true, &Functions::ToString});
add({"TO_BOOL", ".", true, false, true, &Functions::ToBool});
add({"TO_ARRAY", ".", true, false, true, &Functions::ToArray});
// TO_LIST is an alias for TO_ARRAY
addAlias("TO_LIST", "TO_ARRAY");
}
void AqlFunctionFeature::addStringFunctions() {
// string functions
add({"CONCAT", ".|+", true, false, true, &Functions::Concat});
add({"CONCAT_SEPARATOR", ".,.|+", true, false,
true, &Functions::ConcatSeparator});
add({"CHAR_LENGTH", ".", true, false, true, &Functions::CharLength});
add({"LOWER", ".", true, false, true, &Functions::Lower});
add({"UPPER", ".", true, false, true, &Functions::Upper});
add({"SUBSTRING", ".,.|.", true, false, true, &Functions::Substring});
add({"CONTAINS", ".,.|.", true, false, true, &Functions::Contains});
add({"LIKE", ".,.|.", true, false, true, &Functions::Like});
add({"REGEX_TEST", ".,.|.", true, false, true, &Functions::RegexTest});
add({"REGEX_REPLACE", ".,.,.|.", true, false, true, &Functions::RegexReplace});
add({"LEFT", ".,.", true, false, true, &Functions::Left});
add({"RIGHT", ".,.", true, false, true, &Functions::Right});
add({"TRIM", ".|.", true, false, true, &Functions::Trim});
add({"LTRIM", ".|.", true, false, true, &Functions::LTrim});
add({"RTRIM", ".|.", true, false, true, &Functions::RTrim});
add({"FIND_FIRST", ".,.|.,.", true, false, true, &Functions::FindFirst});
add({"FIND_LAST", ".,.|.,.", true, false, true, &Functions::FindLast});
add({"SPLIT", ".|.,.", true, false, true, &Functions::Split});
add({"SUBSTITUTE", ".,.|.,.", true, false, true, &Functions::Substitute});
add({"MD5", ".", true, false, true, &Functions::Md5});
add({"SHA1", ".", true, false, true, &Functions::Sha1});
add({"SHA512", ".", true, false, true, &Functions::Sha512});
add({"HASH", ".", true, false, true, &Functions::Hash});
add({"RANDOM_TOKEN", ".", false, true, true, &Functions::RandomToken});
add({"TO_BASE64", ".", true, false, true, &Functions::ToBase64});
add({"TO_HEX", ".", true, false, true, &Functions::ToHex});
add({"ENCODE_URI_COMPONENT", ".", true, false, true, &Functions::EncodeURIComponent});
add({"UUID", "", true, false, true, &Functions::UUID});
add({"SOUNDEX", ".", true, false, true, &Functions::Soundex});
// FULLTEXT is replaced by the AQL optimizer with an index lookup
add({"FULLTEXT", ".h,.,.|." , false, true, false, &Functions::NotImplemented});
}
void AqlFunctionFeature::addNumericFunctions() {
// numeric functions
add({"FLOOR", ".", true, false, true, &Functions::Floor});
add({"CEIL", ".", true, false, true, &Functions::Ceil});
add({"ROUND", ".", true, false, true, &Functions::Round});
add({"ABS", ".", true, false, true, &Functions::Abs});
add({"RAND", "", false, false, true, &Functions::Rand});
add({"SQRT", ".", true, false, true, &Functions::Sqrt});
add({"POW", ".,.", true, false, true, &Functions::Pow});
add({"LOG", ".", true, false, true, &Functions::Log});
add({"LOG2", ".", true, false, true, &Functions::Log2});
add({"LOG10", ".", true, false, true, &Functions::Log10});
add({"EXP", ".", true, false, true, &Functions::Exp});
add({"EXP2", ".", true, false, true, &Functions::Exp2});
add({"SIN", ".", true, false, true, &Functions::Sin});
add({"COS", ".", true, false, true, &Functions::Cos});
add({"TAN", ".", true, false, true, &Functions::Tan});
add({"ASIN", ".", true, false, true, &Functions::Asin});
add({"ACOS", ".", true, false, true, &Functions::Acos});
add({"ATAN", ".", true, false, true, &Functions::Atan});
add({"ATAN2", ".,.", true, false, true, &Functions::Atan2});
add({"RADIANS", ".", true, false, true, &Functions::Radians});
add({"DEGREES", ".", true, false, true, &Functions::Degrees});
add({"PI", "", true, false, true, &Functions::Pi});
}
void AqlFunctionFeature::addListFunctions() {
// list functions
add({"RANGE", ".,.|.", true, false, true, &Functions::Range});
add({"UNION", ".,.|+", true, false, true, &Functions::Union});
add({"UNION_DISTINCT", ".,.|+", true, false, true, &Functions::UnionDistinct});
add({"MINUS", ".,.|+", true, false, true, &Functions::Minus});
add({"OUTERSECTION", ".,.|+", true, false, true, &Functions::Outersection});
add({"INTERSECTION", ".,.|+", true, false, true, &Functions::Intersection});
add({"FLATTEN", ".|.", true, false, true, &Functions::Flatten});
add({"LENGTH", ".", true, false, true, &Functions::Length});
addAlias("COUNT", "LENGTH");
add({"MIN", ".", true, false, true, &Functions::Min});
add({"MAX", ".", true, false, true, &Functions::Max});
add({"SUM", ".", true, false, true, &Functions::Sum});
add({"MEDIAN", ".", true, false, true, &Functions::Median});
add({"PERCENTILE", ".,.|.", true, false, true, &Functions::Percentile});
add({"AVERAGE", ".", true, false, true, &Functions::Average});
addAlias("AVG", "AVERAGE");
add({"VARIANCE_SAMPLE", ".", true, false, true, &Functions::VarianceSample});
add({"VARIANCE_POPULATION", ".", true, false, true, &Functions::VariancePopulation});
addAlias("VARIANCE", "VARIANCE_POPULATION");
add({"STDDEV_SAMPLE", ".", true, false, true, &Functions::StdDevSample});
add({"STDDEV_POPULATION", ".", true, false, true, &Functions::StdDevPopulation});
addAlias("STDDEV", "STDDEV_POPULATION");
add({"COUNT_DISTINCT", ".", true, false, true, &Functions::CountDistinct});
addAlias("COUNT_UNIQUE", "COUNT_DISTINCT");
add({"UNIQUE", ".", true, false, true, &Functions::Unique});
add({"SORTED_UNIQUE", ".", true, false, true, &Functions::SortedUnique});
add({"SORTED", ".", true, false, true, &Functions::Sorted});
add({"SLICE", ".,.|.", true, false, true, &Functions::Slice});
add({"REVERSE", ".", true, false, true, &Functions::Reverse});
add({"FIRST", ".", true, false, true, &Functions::First});
add({"LAST", ".", true, false, true, &Functions::Last});
add({"NTH", ".,.", true, false, true, &Functions::Nth});
add({"POSITION", ".,.|.", true, false, true, &Functions::Position});
add({"CALL", ".|.+", false, true, false, &Functions::Call});
add({"APPLY", ".|.", false, true, false, &Functions::Apply});
add({"PUSH", ".,.|.", true, false, true, &Functions::Push});
add({"APPEND", ".,.|.", true, false, true, &Functions::Append});
add({"POP", ".", true, false, true, &Functions::Pop});
add({"SHIFT", ".", true, false, true, &Functions::Shift});
add({"UNSHIFT", ".,.|.", true, false, true, &Functions::Unshift});
add({"REMOVE_VALUE", ".,.|.", true, false, true, &Functions::RemoveValue});
add({"REMOVE_VALUES", ".,.", true, false, true, &Functions::RemoveValues});
add({"REMOVE_NTH", ".,.", true, false, true, &Functions::RemoveNth});
}
void AqlFunctionFeature::addDocumentFunctions() {
// document functions
add({"HAS", ".,.", true, false, true, &Functions::Has});
add({"ATTRIBUTES", ".|.,.", true, false, true, &Functions::Attributes});
add({"VALUES", ".|.", true, false, true, &Functions::Values});
add({"MERGE", ".|+", true, false, true, &Functions::Merge});
add({"MERGE_RECURSIVE", ".,.|+", true, false, true, &Functions::MergeRecursive});
add({"DOCUMENT", "h.|.", false, true, false, &Functions::Document});
add({"MATCHES", ".,.|.", true, false, true, &Functions::Matches});
add({"UNSET", ".,.|+", true, false, true, &Functions::Unset});
add({"UNSET_RECURSIVE", ".,.|+", true, false, true, &Functions::UnsetRecursive});
add({"KEEP", ".,.|+", true, false, true, &Functions::Keep});
add({"TRANSLATE", ".,.|.", true, false, true, &Functions::Translate});
add({"ZIP", ".,.", true, false, true, &Functions::Zip});
add({"JSON_STRINGIFY", ".", true, false, true, &Functions::JsonStringify});
add({"JSON_PARSE", ".", true, false, true, &Functions::JsonParse});
}
void AqlFunctionFeature::addGeoFunctions() {
// geo functions
add({"DISTANCE", ".,.,.,.", true, false, true, &Functions::Distance});
add({"IS_IN_POLYGON", ".,.|.", true, false, true, &Functions::IsInPolygon});
add({"GEO_DISTANCE", ".,.", true, false, true, &Functions::GeoDistance});
add({"GEO_CONTAINS", ".,.", true, false, true, &Functions::GeoContains});
add({"GEO_INTERSECTS", ".,.", true, false, true, &Functions::GeoIntersects});
add({"GEO_EQUALS", ".,.", true, false, true, &Functions::GeoEquals});
// NEAR and WITHIN are replaced by the AQL optimizer with collection-based subqueries
add({"NEAR", ".h,.,.|.,.", false, true, false, &Functions::NotImplemented});
add({"WITHIN", ".h,.,.,.|.", false, true, false, &Functions::NotImplemented});
add({"WITHIN_RECTANGLE", "h.,.,.,.,.", false, true, false, &Functions::NotImplemented });
}
void AqlFunctionFeature::addGeometryConstructors() {
// geometry types
add({"GEO_POINT", ".,.", true, false, true, &Functions::GeoPoint});
add({"GEO_MULTIPOINT", ".", true, false, true, &Functions::GeoMultiPoint});
add({"GEO_POLYGON", ".", true, false, true, &Functions::GeoPolygon});
add({"GEO_LINESTRING", ".", true, false, true, &Functions::GeoLinestring});
add({"GEO_MULTILINESTRING", ".", true, false, true, &Functions::GeoMultiLinestring});
}
void AqlFunctionFeature::addDateFunctions() {
// date functions
add({"DATE_NOW", "", false, false, true, &Functions::DateNow});
add({"DATE_TIMESTAMP", ".|.,.,.,.,.,.", true, false, true, &Functions::DateTimestamp});
add({"DATE_ISO8601", ".|.,.,.,.,.,.", true, false, true, &Functions::DateIso8601});
add({"DATE_DAYOFWEEK", ".", true, false, true, &Functions::DateDayOfWeek});
add({"DATE_YEAR", ".", true, false, true, &Functions::DateYear});
add({"DATE_MONTH", ".", true, false, true, &Functions::DateMonth});
add({"DATE_DAY", ".", true, false, true, &Functions::DateDay});
add({"DATE_HOUR", ".", true, false, true, &Functions::DateHour});
add({"DATE_MINUTE", ".", true, false, true, &Functions::DateMinute});
add({"DATE_SECOND", ".", true, false, true, &Functions::DateSecond});
add({"DATE_MILLISECOND", ".", true, false, true, &Functions::DateMillisecond});
add({"DATE_DAYOFYEAR", ".", true, false, true, &Functions::DateDayOfYear});
add({"DATE_ISOWEEK", ".", true, false, true, &Functions::DateIsoWeek});
add({"DATE_LEAPYEAR", ".", true, false, true, &Functions::DateLeapYear});
add({"DATE_QUARTER", ".", true, false, true, &Functions::DateQuarter});
add({"DATE_DAYS_IN_MONTH", ".", true, false, true, &Functions::DateDaysInMonth});
add({"DATE_ADD", ".,.|.", true, false, true, &Functions::DateAdd});
add({"DATE_SUBTRACT", ".,.|.", true, false, true, &Functions::DateSubtract});
add({"DATE_DIFF", ".,.,.|.", true, false, true, &Functions::DateDiff});
add({"DATE_COMPARE", ".,.,.|.", true, false, true, &Functions::DateCompare});
add({"DATE_FORMAT", ".,.", true, false, true, &Functions::DateFormat});
add({"DATE_TRUNC", ".,.", true, false, true, &Functions::DateTrunc});
}
void AqlFunctionFeature::addMiscFunctions() {
// misc functions
add({"FAIL", "|.", false, true, true, &Functions::Fail});
add({"PASSTHRU", ".", false, false, true, &Functions::Passthru});
addAlias("NOOPT", "PASSTHRU");
add({"V8", ".", true, false, true });
add({"SLEEP", ".", false, true, true, &Functions::Sleep});
add({"COLLECTIONS", "", false, true, false, &Functions::Collections});
add({"NOT_NULL", ".|+", true, false, true, &Functions::NotNull});
add({"FIRST_LIST", ".|+", true, false, true, &Functions::FirstList});
add({"FIRST_DOCUMENT", ".|+", true, false, true, &Functions::FirstDocument});
add({"PARSE_IDENTIFIER", ".", true, false, true, &Functions::ParseIdentifier});
add({"IS_SAME_COLLECTION", ".h,.h", true, false, true, &Functions::IsSameCollection});
add({"CURRENT_USER", "", false, false, false, &Functions::CurrentUser});
add({"CURRENT_DATABASE", "", false, false, false, &Functions::CurrentDatabase});
add({"COLLECTION_COUNT", ".h", false, true, false, &Functions::CollectionCount});
add({"ASSERT", ".,.", false, true, true, &Functions::Assert});
add({"WARN", ".,.", false, false, true, &Functions::Warn});
}
void AqlFunctionFeature::addStorageEngineFunctions() {
StorageEngine* engine = EngineSelectorFeature::ENGINE;
TRI_ASSERT(engine != nullptr); // Engine not loaded. Startup broken
engine->addAqlFunctions();
}