1
0
Fork 0

AQL user functions

This commit is contained in:
Jan Steemann 2013-04-05 12:50:44 +02:00
parent 31f4edde25
commit ed10d9b6aa
27 changed files with 1124 additions and 62 deletions

View File

@ -210,6 +210,7 @@ endif
################################################################################
SHELL_COMMON = @top_srcdir@/js/common/tests/shell-require.js \
@top_srcdir@/js/common/tests/shell-aqlfunctions.js \
@top_srcdir@/js/common/tests/shell-attributes.js \
@top_srcdir@/js/common/tests/shell-collection.js \
@top_srcdir@/js/common/tests/shell-collection-volatile.js \

View File

@ -100,6 +100,64 @@ inline static void InitNode (TRI_aql_context_t* const context,
TRI_RegisterNodeContextAql(context, node);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST function call node
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_node_t* CreateNodeInternalFcall (TRI_aql_context_t* const context,
const char* const name,
const char* const internalName,
const TRI_aql_node_t* const parameters) {
CREATE_NODE(TRI_AQL_NODE_FCALL)
TRI_AQL_NODE_DATA(node) = 0;
{
TRI_aql_function_t* function;
TRI_associative_pointer_t* functions;
assert(context->_vocbase);
functions = context->_vocbase->_functions;
assert(functions);
function = TRI_GetByExternalNameFunctionAql(functions, internalName);
if (function == NULL) {
// function name is unknown
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_FUNCTION_NAME_UNKNOWN, name);
return NULL;
}
// validate function call arguments
if (! TRI_ValidateArgsFunctionAql(context, function, parameters)) {
return NULL;
}
// initialise
ADD_MEMBER(parameters)
TRI_AQL_NODE_DATA(node) = function;
}
return node;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST function call node
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_node_t* CreateNodeUserFcall (TRI_aql_context_t* const context,
const char* const name,
char* const internalName,
const TRI_aql_node_t* const parameters) {
CREATE_NODE(TRI_AQL_NODE_FCALL_USER)
// we'll take ownership of the function name now
TRI_AQL_NODE_STRING(node) = internalName;
ADD_MEMBER(parameters)
return node;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
@ -934,38 +992,33 @@ TRI_aql_node_t* TRI_CreateNodeArrayElementAql (TRI_aql_context_t* const context,
TRI_aql_node_t* TRI_CreateNodeFcallAql (TRI_aql_context_t* const context,
const char* const name,
const TRI_aql_node_t* const parameters) {
CREATE_NODE(TRI_AQL_NODE_FCALL)
// initialise
TRI_AQL_NODE_DATA(node) = NULL;
TRI_aql_node_t* node;
char* upperName;
if (name == NULL) {
ABORT_OOM
}
{
TRI_aql_function_t* function;
TRI_associative_pointer_t* functions;
if (strchr(name, TRI_AQL_NAMESPACE_SEPARATOR_CHAR) != NULL) {
upperName = TRI_UpperAsciiStringZ(TRI_CORE_MEM_ZONE, name);
}
else {
upperName = TRI_Concatenate2String(TRI_AQL_DEFAULT_PREFIX, TRI_UpperAsciiStringZ(TRI_UNKNOWN_MEM_ZONE, name));
}
assert(context->_vocbase);
functions = context->_vocbase->_functions;
assert(functions);
function = TRI_GetByExternalNameFunctionAql(functions, name);
if (! function) {
// function name is unknown
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_FUNCTION_NAME_UNKNOWN, name);
return NULL;
}
// validate function call arguments
if (! TRI_ValidateArgsFunctionAql(context, function, parameters)) {
return NULL;
}
ADD_MEMBER(parameters)
TRI_AQL_NODE_DATA(node) = function;
if (upperName == NULL) {
ABORT_OOM
}
if (*upperName == '_') {
// default internal namespace
node = CreateNodeInternalFcall(context, name, upperName, parameters);
TRI_Free(TRI_CORE_MEM_ZONE, upperName);
}
else {
// user namespace
node = CreateNodeUserFcall(context, name, upperName, parameters);
// upperName intentionally not freed!
}
return node;

View File

@ -1427,7 +1427,8 @@ static void ProcessArgList (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, ", ");
}
if (parameter->_type == TRI_AQL_NODE_COLLECTION &&
if (function != NULL &&
parameter->_type == TRI_AQL_NODE_COLLECTION &&
TRI_ConvertParameterFunctionAql(function, i)) {
// collection arguments will be created as string argument => e.g. "users"
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(parameter, 0);
@ -1880,8 +1881,8 @@ static void ProcessSubquery (TRI_aql_codegen_js_t* const generator,
/// @brief generate code for function calls
////////////////////////////////////////////////////////////////////////////////
static void ProcessFcall (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
static void ProcessFcallInternal (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "aql.");
ScopeOutput(generator, TRI_GetInternalNameFunctionAql((TRI_aql_function_t*) TRI_AQL_NODE_DATA(node)));
ScopeOutput(generator, "(");
@ -1889,6 +1890,19 @@ static void ProcessFcall (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, ")");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for function calls
////////////////////////////////////////////////////////////////////////////////
static void ProcessFcallUser (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
ScopeOutput(generator, "aql.FCALL_USER(");
ScopeOutputQuoted(generator, TRI_AQL_NODE_STRING(node));
ScopeOutput(generator, ", [");
ProcessArgList(generator, NULL, TRI_AQL_NODE_MEMBER(node, 0));
ScopeOutput(generator, "])");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for a scope start
////////////////////////////////////////////////////////////////////////////////
@ -2402,7 +2416,10 @@ static void ProcessNode (TRI_aql_codegen_js_t* const generator, const TRI_aql_no
ProcessTernary(generator, node);
break;
case TRI_AQL_NODE_FCALL:
ProcessFcall(generator, node);
ProcessFcallInternal(generator, node);
break;
case TRI_AQL_NODE_FCALL_USER:
ProcessFcallUser(generator, node);
break;
case TRI_AQL_NODE_FOR:
ProcessFor(generator, node);

View File

@ -423,7 +423,57 @@ char* TRI_RegisterStringAql (TRI_aql_context_t* const context,
ABORT_OOM
}
TRI_PushBackVectorPointer(&context->_memory._strings, copy);
if (TRI_PushBackVectorPointer(&context->_memory._strings, copy) != TRI_ERROR_NO_ERROR) {
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, copy);
ABORT_OOM
}
return copy;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief register a combined string
////////////////////////////////////////////////////////////////////////////////
char* TRI_RegisterString2Aql (TRI_aql_context_t* const context,
const char* const s1,
const char* const s2) {
char* copy;
copy = TRI_Concatenate2StringZ(TRI_UNKNOWN_MEM_ZONE, s1, s2);
if (copy == NULL) {
ABORT_OOM
}
if (TRI_PushBackVectorPointer(&context->_memory._strings, copy) != TRI_ERROR_NO_ERROR) {
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, copy);
ABORT_OOM
}
return copy;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief register a combined string
////////////////////////////////////////////////////////////////////////////////
char* TRI_RegisterString3Aql (TRI_aql_context_t* const context,
const char* const s1,
const char* const s2,
const char* const s3) {
char* copy;
copy = TRI_Concatenate3StringZ(TRI_UNKNOWN_MEM_ZONE, s1, s2, s3);
if (copy == NULL) {
ABORT_OOM
}
if (TRI_PushBackVectorPointer(&context->_memory._strings, copy) != TRI_ERROR_NO_ERROR) {
TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, copy);
ABORT_OOM
}
return copy;
}

View File

@ -143,6 +143,23 @@ char* TRI_RegisterStringAql (TRI_aql_context_t* const,
const size_t,
const bool);
////////////////////////////////////////////////////////////////////////////////
/// @brief register a combined string
////////////////////////////////////////////////////////////////////////////////
char* TRI_RegisterString2Aql (TRI_aql_context_t* const,
const char* const,
const char* const);
////////////////////////////////////////////////////////////////////////////////
/// @brief register a combined string
////////////////////////////////////////////////////////////////////////////////
char* TRI_RegisterString3Aql (TRI_aql_context_t* const,
const char* const,
const char* const,
const char* const);
////////////////////////////////////////////////////////////////////////////////
/// @brief register a node
////////////////////////////////////////////////////////////////////////////////

View File

@ -603,6 +603,22 @@ bool TRI_NodeStringAql (TRI_string_buffer_t* const buffer,
return TRI_AppendStringStringBuffer(buffer, ")") == TRI_ERROR_NO_ERROR;
}
case TRI_AQL_NODE_FCALL_USER: {
if (TRI_AppendStringStringBuffer(buffer, TRI_AQL_NODE_STRING(node)) != TRI_ERROR_NO_ERROR) {
return false;
}
if (TRI_AppendStringStringBuffer(buffer, "(") != TRI_ERROR_NO_ERROR) {
return false;
}
if (! TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 0))) {
return false;
}
return TRI_AppendStringStringBuffer(buffer, ")") == TRI_ERROR_NO_ERROR;
}
case TRI_AQL_NODE_EXPAND: {
return TRI_NodeStringAql(buffer, TRI_AQL_NODE_MEMBER(node, 3));
}

View File

@ -50,7 +50,7 @@
////////////////////////////////////////////////////////////////////////////////
#define REGISTER_FUNCTION(internalName, externalName, deterministic, group, argPattern, optimiseCallback) \
result &= TRI_RegisterFunctionAql(functions, internalName, externalName, deterministic, group, argPattern, optimiseCallback)
result &= TRI_RegisterFunctionAql(functions, TRI_AQL_DEFAULT_PREFIX internalName, externalName, deterministic, group, argPattern, optimiseCallback)
////////////////////////////////////////////////////////////////////////////////
/// @brief shorthand to check an argument and return an error if it is invalid
@ -680,10 +680,11 @@ void TRI_FreeFunctionsAql (TRI_associative_pointer_t* functions) {
for (i = 0; i < functions->_nrAlloc; ++i) {
TRI_aql_function_t* function = (TRI_aql_function_t*) functions->_table[i];
if (!function) {
if (function == NULL) {
continue;
}
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_argPattern);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_externalName);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_internalName);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function);
@ -707,13 +708,14 @@ TRI_aql_function_t* TRI_GetByExternalNameFunctionAql (TRI_associative_pointer_t*
// normalize the name by upper-casing it
upperName = TRI_UpperAsciiStringZ(TRI_UNKNOWN_MEM_ZONE, externalName);
if (upperName == NULL) {
return NULL;
}
function = (TRI_aql_function_t*) TRI_LookupByKeyAssociativePointer(functions, (void*) upperName);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, upperName);
return function;
}
@ -744,21 +746,30 @@ bool TRI_RegisterFunctionAql (TRI_associative_pointer_t* functions,
return false;
}
function->_externalName = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, externalName);
function->_externalName = TRI_UpperAsciiStringZ(TRI_UNKNOWN_MEM_ZONE, externalName);
if (function->_externalName == NULL) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function);
return false;
}
// normalize name by upper-casing it
function->_internalName = TRI_UpperAsciiStringZ(TRI_UNKNOWN_MEM_ZONE, internalName);
function->_internalName = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, internalName);
if (function->_internalName == NULL) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_externalName);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function);
return false;
}
function->_argPattern = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, argPattern);
if (function->_argPattern == NULL) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_internalName);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_externalName);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function);
return false;
}
if (TRI_InsertKeyAssociativePointer(functions, externalName, function, false)) {
if (TRI_InsertKeyAssociativePointer(functions, function->_externalName, function, false)) {
// function already registered
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_externalName);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, function->_internalName);
@ -768,7 +779,6 @@ bool TRI_RegisterFunctionAql (TRI_associative_pointer_t* functions,
function->_isDeterministic = isDeterministic;
function->_isGroup = isGroup;
function->_argPattern = argPattern;
function->optimise = optimise;
// set minArgs and maxArgs

View File

@ -36,10 +36,55 @@
extern "C" {
#endif
// -----------------------------------------------------------------------------
// --SECTION-- forward declarations
// -----------------------------------------------------------------------------
struct TRI_aql_context_s;
struct TRI_aql_field_access_s;
struct TRI_associative_pointer_s;
// -----------------------------------------------------------------------------
// --SECTION-- public defines
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief default namespace for aql functions
////////////////////////////////////////////////////////////////////////////////
#define TRI_AQL_DEFAULT_NAMESPACE "_AQL"
////////////////////////////////////////////////////////////////////////////////
/// @brief separator between namespace and function name
////////////////////////////////////////////////////////////////////////////////
#define TRI_AQL_NAMESPACE_SEPARATOR ":"
////////////////////////////////////////////////////////////////////////////////
/// @brief separator between namespace and function name
////////////////////////////////////////////////////////////////////////////////
#define TRI_AQL_NAMESPACE_SEPARATOR_CHAR ':'
////////////////////////////////////////////////////////////////////////////////
/// @brief default namespace for aql functions
////////////////////////////////////////////////////////////////////////////////
#define TRI_AQL_DEFAULT_PREFIX TRI_AQL_DEFAULT_NAMESPACE TRI_AQL_NAMESPACE_SEPARATOR
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public types
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
@ -54,13 +99,26 @@ typedef struct TRI_aql_function_s {
char* _internalName;
bool _isDeterministic;
bool _isGroup;
const char* _argPattern;
char* _argPattern;
size_t _minArgs;
size_t _maxArgs;
void (*optimise)(const TRI_aql_node_t* const, struct TRI_aql_context_s* const, struct TRI_aql_field_access_s*);
}
TRI_aql_function_t;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief initialise the array with the function declarations
////////////////////////////////////////////////////////////////////////////////

View File

@ -149,6 +149,7 @@ void Ahuacatlerror (YYLTYPE* locp, TRI_aql_context_t* const context, const char*
%type <node> operator_binary;
%type <node> operator_ternary;
%type <node> function_call;
%type <strval> function_name;
%type <node> optional_function_call_arguments;
%type <node> function_arguments_list;
%type <node> compound_type;
@ -459,10 +460,30 @@ expression:
}
;
function_call:
function_name:
T_STRING {
$$ = $1;
if ($$ == NULL) {
ABORT_OOM
}
}
| function_name T_COLON T_STRING {
if ($1 == NULL || $3 == NULL) {
ABORT_OOM
}
$$ = TRI_RegisterString3Aql(context, $1, ":", $3);
if ($$ == NULL) {
ABORT_OOM
}
}
;
function_call:
function_name {
TRI_aql_node_t* node;
/* function call */
if (! TRI_PushStackParseAql(context, $1)) {
ABORT_OOM

View File

@ -183,6 +183,8 @@ const char* TRI_NodeNameAql (const TRI_aql_node_type_e type) {
return "parameter";
case TRI_AQL_NODE_FCALL:
return "function call";
case TRI_AQL_NODE_FCALL_USER:
return "function call (user)";
}
assert(false);

View File

@ -182,7 +182,8 @@ typedef enum {
TRI_AQL_NODE_REFERENCE,
TRI_AQL_NODE_ATTRIBUTE,
TRI_AQL_NODE_PARAMETER,
TRI_AQL_NODE_FCALL
TRI_AQL_NODE_FCALL,
TRI_AQL_NODE_FCALL_USER
}
TRI_aql_node_type_e;

View File

@ -148,9 +148,13 @@ static TRI_aql_node_t* DumpNode (TRI_aql_statement_walker_t* const walker,
case TRI_AQL_NODE_ATTRIBUTE_ACCESS:
DumpString(state, node);
break;
case TRI_AQL_NODE_FCALL:
printf("name: %s\n", TRI_GetInternalNameFunctionAql((TRI_aql_function_t*) TRI_AQL_NODE_DATA(node)));
break;
case TRI_AQL_NODE_FCALL_USER:
printf("name: %s\n", TRI_AQL_NODE_STRING(node));
break;
case TRI_AQL_NODE_SORT_ELEMENT:
PrintIndent(state);

View File

@ -29,6 +29,8 @@
#include "Basics/ConditionLocker.h"
#include "Basics/ReadLocker.h"
#include "Basics/MutexLocker.h"
#include "Basics/Mutex.h"
#include "Basics/StringUtils.h"
#include "Basics/WriteLocker.h"
#include "Logger/Logger.h"
@ -121,6 +123,8 @@ namespace {
////////////////////////////////////////////////////////////////////////////////
void ApplicationV8::V8Context::addGlobalContextMethod (string const& method) {
MUTEX_LOCKER(_globalMethodsLock);
_globalMethods.push_back(method);
}
@ -130,6 +134,8 @@ void ApplicationV8::V8Context::addGlobalContextMethod (string const& method) {
void ApplicationV8::V8Context::handleGlobalContextMethods () {
v8::HandleScope scope;
MUTEX_LOCKER(_globalMethodsLock);
for (vector<string>::iterator i = _globalMethods.begin(); i != _globalMethods.end(); ++i) {
string const& func = *i;
@ -278,6 +284,7 @@ ApplicationV8::V8Context* ApplicationV8::enterContext (bool initialise) {
context->_isolate->Enter();
context->_context->Enter();
LOGGER_TRACE("entering V8 context " << context->_id);
context->handleGlobalContextMethods();
if (_developmentMode && ! initialise) {
@ -300,6 +307,7 @@ void ApplicationV8::exitContext (V8Context* context) {
V8GcThread* gc = dynamic_cast<V8GcThread*>(_gcThread);
assert(gc != 0);
LOGGER_TRACE("leaving V8 context " << context->_id);
double lastGc = gc->getLastGcStamp();
CONDITION_LOCKER(guard, _contextCondition);
@ -337,24 +345,8 @@ void ApplicationV8::exitContext (V8Context* context) {
////////////////////////////////////////////////////////////////////////////////
void ApplicationV8::addGlobalContextMethod (string const& method) {
CONDITION_LOCKER(guard, _contextCondition);
for (vector<V8Context*>::iterator i = _freeContexts.begin(); i != _freeContexts.end(); ++i) {
V8Context* context = *i;
context->addGlobalContextMethod(method);
}
for (vector<V8Context*>::iterator i = _dirtyContexts.begin(); i != _dirtyContexts.end(); ++i) {
V8Context* context = *i;
context->addGlobalContextMethod(method);
}
for (set<V8Context*>::iterator i = _busyContexts.begin(); i != _busyContexts.end(); ++i) {
V8Context* context = *i;
context->addGlobalContextMethod(method);
for (size_t i = 0; i < _nrInstances; ++i) {
_contexts[i]->addGlobalContextMethod(method);
}
}

View File

@ -129,6 +129,12 @@ namespace triagens {
void handleGlobalContextMethods ();
////////////////////////////////////////////////////////////////////////////////
/// @brief mutex to protect _globalMethods
////////////////////////////////////////////////////////////////////////////////
basics::Mutex _globalMethodsLock;
////////////////////////////////////////////////////////////////////////////////
/// @brief open global methods
////////////////////////////////////////////////////////////////////////////////

View File

@ -110,6 +110,9 @@
"ERROR_QUERY_FAIL_CALLED" : { "code" : 1569, "message" : "FAIL(%s) called" },
"ERROR_QUERY_GEO_INDEX_MISSING" : { "code" : 1570, "message" : "no suitable geo index found for geo restriction on '%s'" },
"ERROR_QUERY_FULLTEXT_INDEX_MISSING" : { "code" : 1571, "message" : "no suitable fulltext index found for fulltext query on '%s'" },
"ERROR_QUERY_FUNCTION_INVALID_NAME" : { "code" : 1580, "message" : "invalid user function name" },
"ERROR_QUERY_FUNCTION_INVALID_CODE" : { "code" : 1581, "message" : "invalid user function code" },
"ERROR_QUERY_FUNCTION_NOT_FOUND" : { "code" : 1582, "message" : "user function not found" },
"ERROR_CURSOR_NOT_FOUND" : { "code" : 1600, "message" : "cursor not found" },
"ERROR_TRANSACTION_INCOMPLETE" : { "code" : 1650, "message" : "transaction definition is incomplete" },
"ERROR_TRANSACTION_INVALID_STATE" : { "code" : 1651, "message" : "invalid transaction state" },

View File

@ -80,6 +80,19 @@
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief reloads the AQL user functions
////////////////////////////////////////////////////////////////////////////////
internal.reloadAqlFunctions = function () {
if (typeof internal.arango !== 'undefined') {
internal.arango.POST("/_admin/aql/reload", "");
return;
}
throw "not connected";
};
////////////////////////////////////////////////////////////////////////////////
/// @brief flushes the module cache of the server
////////////////////////////////////////////////////////////////////////////////

View File

@ -158,6 +158,21 @@ actions.defineHttp({
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief reloads the AQL user functions
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_admin/aql/reload",
context : "admin",
prefix : false,
callback : function (req, res) {
internal.reloadAqlFunctions();
actions.resultOk(req, res, actions.HTTP_OK);
}
});
////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_GET_admin_routing_reloads
/// @brief reloads the routing information

View File

@ -80,6 +80,19 @@
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief reloads the AQL user functions
////////////////////////////////////////////////////////////////////////////////
internal.reloadAqlFunctions = function () {
if (typeof internal.arango !== 'undefined') {
internal.arango.POST("/_admin/aql/reload", "");
return;
}
throw "not connected";
};
////////////////////////////////////////////////////////////////////////////////
/// @brief flushes the module cache of the server
////////////////////////////////////////////////////////////////////////////////

View File

@ -110,6 +110,9 @@
"ERROR_QUERY_FAIL_CALLED" : { "code" : 1569, "message" : "FAIL(%s) called" },
"ERROR_QUERY_GEO_INDEX_MISSING" : { "code" : 1570, "message" : "no suitable geo index found for geo restriction on '%s'" },
"ERROR_QUERY_FULLTEXT_INDEX_MISSING" : { "code" : 1571, "message" : "no suitable fulltext index found for fulltext query on '%s'" },
"ERROR_QUERY_FUNCTION_INVALID_NAME" : { "code" : 1580, "message" : "invalid user function name" },
"ERROR_QUERY_FUNCTION_INVALID_CODE" : { "code" : 1581, "message" : "invalid user function code" },
"ERROR_QUERY_FUNCTION_NOT_FOUND" : { "code" : 1582, "message" : "user function not found" },
"ERROR_CURSOR_NOT_FOUND" : { "code" : 1600, "message" : "cursor not found" },
"ERROR_TRANSACTION_INCOMPLETE" : { "code" : 1650, "message" : "transaction definition is incomplete" },
"ERROR_TRANSACTION_INVALID_STATE" : { "code" : 1651, "message" : "invalid transaction state" },

View File

@ -0,0 +1,226 @@
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
/*global require, exports */
////////////////////////////////////////////////////////////////////////////////
/// @brief AQL user functions management
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 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
////////////////////////////////////////////////////////////////////////////////
var internal = require("internal");
var arangodb = require("org/arangodb");
var db = arangodb.db;
var ArangoError = require("org/arangodb/arango-error").ArangoError;
// -----------------------------------------------------------------------------
// --SECTION-- module "org/arangodb/aql/functions"
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief validate a function name
////////////////////////////////////////////////////////////////////////////////
var validateName = function (name) {
if (typeof name !== 'string' ||
! name.match(/^[a-zA-Z0-9_]+(:[a-zA-Z0-9_]+)+$/) ||
name.substr(0, 1) === "_") {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_QUERY_FUNCTION_INVALID_NAME.code;
err.errorMessage = arangodb.errors.ERROR_QUERY_FUNCTION_INVALID_NAME.message;
throw err;
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief validate user function code
////////////////////////////////////////////////////////////////////////////////
var stringifyFunction = function (code, name) {
if (typeof code === 'function') {
code = String(code);
}
if (typeof code === 'string') {
code = "(" + code + ")";
if (! internal.parse) {
// no parsing possible. assume always valid
return code;
}
try {
if (internal.parse(code, name)) {
// parsing successful
return code;
}
}
catch (e) {
}
}
// fall-through intentional
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_QUERY_FUNCTION_INVALID_CODE.code;
err.errorMessage = arangodb.errors.ERROR_QUERY_FUNCTION_INVALID_CODE.message;
throw err;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief return the _aqlfunctions collection
////////////////////////////////////////////////////////////////////////////////
var getStorage = function () {
var functions = db._collection("_aqlfunctions");
if (functions === null) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code;
err.errorMessage = "collection _aqlfunctions not found";
throw err;
}
return functions;
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief delete an existing AQL user function
////////////////////////////////////////////////////////////////////////////////
var unregisterFunction = function (name) {
var func = null;
validateName(name);
try {
func = getStorage().document(name.toUpperCase());
}
catch (err1) {
}
if (func === null) {
var err = new ArangoError();
err.errorNum = arangodb.errors.ERROR_QUERY_FUNCTION_NOT_FOUND.code;
err.errorMessage = arangodb.errors.ERROR_QUERY_FUNCTION_NOT_FOUND.message;
throw err;
}
getStorage().remove(func._id);
internal.reloadAqlFunctions();
return true;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief register an AQL user function
////////////////////////////////////////////////////////////////////////////////
var registerFunction = function (name, code, isDeterministic) {
// validate input
validateName(name);
code = stringifyFunction(code, name);
try {
unregisterFunction(name);
}
catch (err) {
}
var data = {
_key: name.toUpperCase(),
name: name,
code: code,
isDeterministic: isDeterministic || false
};
getStorage().save(data);
internal.reloadAqlFunctions();
return true;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief reloads the AQL user functons
////////////////////////////////////////////////////////////////////////////////
var reloadFunctions = function () {
throw "cannot use abstract reload function";
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- module exports
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
exports.register = registerFunction;
exports.unregister = unregisterFunction;
exports.reload = reloadFunctions;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\|/\\*jslint"
// End:

View File

@ -0,0 +1,346 @@
/*jslint indent: 2, nomen: true, maxlen: 80 */
/*global require, assertEqual, assertTrue */
////////////////////////////////////////////////////////////////////////////////
/// @brief test the AQL user functions management
///
/// @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
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var arangodb = require("org/arangodb");
var ERRORS = arangodb.errors;
var db = arangodb.db;
var aqlfunctions = require("org/arangodb/aql/functions");
// -----------------------------------------------------------------------------
// --SECTION-- AQL user functions tests
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function AqlFunctionsSuite () {
var unregister = function (name) {
try {
aqlfunctions.unregister(name);
}
catch (err) {
}
};
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function
////////////////////////////////////////////////////////////////////////////////
testRegisterFunc1 : function () {
unregister("UnitTests:tryme:foo");
aqlfunctions.register("UnitTests:tryme:foo", function (what) { return what * 2; }, true);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function
////////////////////////////////////////////////////////////////////////////////
testRegisterFunc2 : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return what * 2; }, true);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function
////////////////////////////////////////////////////////////////////////////////
testRegisterString1 : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", "function (what) { return what * 2; }", true);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function
////////////////////////////////////////////////////////////////////////////////
testRegisterString2 : function () {
unregister("UnitTests:tryme:foo");
aqlfunctions.register("UnitTests:tryme:foo", "function (what) { return what * 2; }", true);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief re-register a function
////////////////////////////////////////////////////////////////////////////////
testReRegister : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return what * 2; }, true);
aqlfunctions.register("UnitTests:tryme", function (what) { return what * 2; }, true);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function with an invalid name
////////////////////////////////////////////////////////////////////////////////
testRegisterInvalidName1 : function () {
try {
aqlfunctions.register("foo", function (what) { return what * 2; }, true);
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function with an invalid name
////////////////////////////////////////////////////////////////////////////////
testRegisterInvalidName2 : function () {
try {
aqlfunctions.register("_test", function (what) { return what * 2; }, true);
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function with an invalid name
////////////////////////////////////////////////////////////////////////////////
testRegisterInvalidName3 : function () {
try {
aqlfunctions.register("_test:foo", function (what) { return what * 2; }, true);
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function with an invalid name
////////////////////////////////////////////////////////////////////////////////
testRegisterInvalidName4 : function () {
try {
aqlfunctions.register("test:", function (what) { return what * 2; }, true);
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_NAME.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function with an invalid body
////////////////////////////////////////////////////////////////////////////////
testRegisterInvalidCode1 : function () {
unregister("UnitTests:tryme");
try {
aqlfunctions.register("UnitTests:tryme", "function (what) { ", true);
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_CODE.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function with an invalid body
////////////////////////////////////////////////////////////////////////////////
testRegisterInvalidCode2 : function () {
unregister("UnitTests:tryme");
try {
aqlfunctions.register("UnitTests:tryme", 1234, true);
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_QUERY_FUNCTION_INVALID_CODE.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and unregister it
////////////////////////////////////////////////////////////////////////////////
testUnregister : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return what * 4; }, true);
aqlfunctions.unregister("UnitTests:tryme");
},
////////////////////////////////////////////////////////////////////////////////
/// @brief unregister an unknown function
////////////////////////////////////////////////////////////////////////////////
testUnregisterUnknown : function () {
unregister("UnitTests:tryme");
try {
aqlfunctions.unregister("UnitTests:tryme");
fail();
}
catch (err) {
assertEqual(ERRORS.ERROR_QUERY_FUNCTION_NOT_FOUND.code, err.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQuery1 : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return what * 2; }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme(4)" }).execute().toArray();
assertEqual([ 8 ], actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQuery2 : function () {
unregister("UnitTests:tryme");
unregister("UnitTests:foo");
aqlfunctions.register("UnitTests:tryme", function (what) { return what * 2; }, true);
aqlfunctions.register("UnitTests:foo", function (what) { return what * 4; }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme(4) + UnitTests:foo(9)" }).execute().toArray();
assertEqual([ 4 * 2 + 9 * 4 ], actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQueryReturnUndefined : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme(4)" }).execute().toArray();
assertEqual([ null ], actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQueryReturnNan : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return 1 / 0; }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme(4)" }).execute().toArray();
assertEqual([ null ], actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQueryReturnNull : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return null; }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme(4)" }).execute().toArray();
assertEqual([ null ], actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQueryReturnTrue : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return true; }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme(4)" }).execute().toArray();
assertEqual([ true ], actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQueryReturnFalse : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return false; }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme(4)" }).execute().toArray();
assertEqual([ false ], actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief register a function and run a query
////////////////////////////////////////////////////////////////////////////////
testQueryReturnComplex : function () {
unregister("UnitTests:tryme");
aqlfunctions.register("UnitTests:tryme", function (what) { return [ true, false, null, 1, 2, -4, [ 5.5, { a: 1, "b": "def" } ] ] }, true);
var actual = db._createStatement({ query: "RETURN UnitTests:tryme()" }).execute().toArray();
assertEqual([ [ true, false, null, 1, 2, -4, [ 5.5, { a: 1, "b": "def" } ] ] ], actual);
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(AqlFunctionsSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -191,6 +191,15 @@
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief reloads the AQL user functions
////////////////////////////////////////////////////////////////////////////////
internal.reloadAqlFunctions = function () {
internal.executeGlobalContextFunction("require(\"org/arangodb/ahuacatl\").reload();");
require("org/arangodb/ahuacatl").reload();
};
////////////////////////////////////////////////////////////////////////////////
/// @brief executes a string in all V8 contexts
////////////////////////////////////////////////////////////////////////////////

View File

@ -34,8 +34,6 @@ var INTERNAL = require("internal");
var TRAVERSAL = require("org/arangodb/graph/traversal");
var ArangoError = require("org/arangodb/arango-error").ArangoError;
var RegexCache = { 'i' : { }, '' : { } };
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
@ -45,6 +43,18 @@ var RegexCache = { 'i' : { }, '' : { } };
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief cache for compiled regexes
////////////////////////////////////////////////////////////////////////////////
var RegexCache = { };
////////////////////////////////////////////////////////////////////////////////
/// @brief user functions cache
////////////////////////////////////////////////////////////////////////////////
var UserFunctions = { };
////////////////////////////////////////////////////////////////////////////////
/// @brief type weight used for sorting and comparing
////////////////////////////////////////////////////////////////////////////////
@ -209,6 +219,44 @@ function CLONE (obj) {
return copy;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief box a value into the AQL datatype system
////////////////////////////////////////////////////////////////////////////////
function FIX_VALUE (value) {
var type = typeof(value), i;
if (value === undefined ||
value === null ||
(type === 'number' && (isNaN(value) || ! isFinite(value)))) {
return null;
}
if (type === 'boolean' || type === 'string' || type === 'number') {
return value;
}
if (Array.isArray(value)) {
for (i = 0; i < value.length; ++i) {
value[i] = FIX_VALUE(value[i]);
}
return value;
}
if (type === 'object') {
for (i in value) {
if (value.hasOwnProperty(i)) {
value[i] = FIX_VALUE(value[i]);
}
}
return value;
}
return null;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the sort type of an operand
////////////////////////////////////////////////////////////////////////////////
@ -317,6 +365,20 @@ function FCALL (name, parameters) {
return name.apply(null, parameters);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief call a user function
////////////////////////////////////////////////////////////////////////////////
function FCALL_USER (name, parameters) {
if (UserFunctions.hasOwnProperty(name)) {
var result = UserFunctions[name].func.apply(null, parameters);
return FIX_VALUE(result);
}
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_NOT_FOUND, name);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the numeric value or undefined if it is out of range
////////////////////////////////////////////////////////////////////////////////
@ -3220,6 +3282,71 @@ function GRAPH_EDGES (edgeCollection,
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- setup / reset functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief reset the regex cache
////////////////////////////////////////////////////////////////////////////////
function resetRegexCache () {
RegexCache = { 'i' : { }, '' : { } };
}
////////////////////////////////////////////////////////////////////////////////
/// @brief reset the user functions and reload them from the database
////////////////////////////////////////////////////////////////////////////////
function reloadUserFunctions () {
var c;
UserFunctions = { };
c = INTERNAL.db._collection("_aqlfunctions");
if (c === null) {
return;
}
c.toArray().forEach(function (f) {
var code;
code = "(function() { var callback = " + f.code + "; return callback; })();";
try {
var res = INTERNAL.executeScript(code, undefined, "(user function " + f._key + ")");
UserFunctions[f._key.toUpperCase()] = {
name: f._key,
func: res,
isDeterministic: f.isDeterministic || false
};
}
catch (err) {
THROW(INTERNAL.errors.ERROR_QUERY_FUNCTION_INVALID_CODE);
}
});
}
////////////////////////////////////////////////////////////////////////////////
/// @brief reset the query engine
////////////////////////////////////////////////////////////////////////////////
function resetEngine () {
resetRegexCache();
reloadUserFunctions();
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- MODULE EXPORTS
// -----------------------------------------------------------------------------
@ -3230,6 +3357,7 @@ function GRAPH_EDGES (edgeCollection,
////////////////////////////////////////////////////////////////////////////////
exports.FCALL = FCALL;
exports.FCALL_USER = FCALL_USER;
exports.KEYS = KEYS;
exports.GET_INDEX = GET_INDEX;
exports.DOCUMENT_MEMBER = DOCUMENT_MEMBER;
@ -3331,10 +3459,16 @@ exports.MATCHES = MATCHES;
exports.PASSTHRU = PASSTHRU;
exports.FAIL = FAIL;
exports.reload = reloadUserFunctions;
// initialise the query engine
resetEngine();
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -344,6 +344,11 @@
return true;
});
// set up the collection _aqlfunctions
addTask("setupAqlFunctions", "setup _aqlfunctions collection", function () {
return createSystemCollection("_aqlfunctions", { waitForSync : false });
});
// loop through all tasks and execute them
console.log("Found " + allTasks.length + " defined task(s), "

View File

@ -141,6 +141,14 @@ ERROR_QUERY_FAIL_CALLED,1569,"FAIL(%s) called","Will be raised when the function
ERROR_QUERY_GEO_INDEX_MISSING,1570,"no suitable geo index found for geo restriction on '%s'","Will be raised when a geo restriction was specified but no suitable geo index is found to resolve it."
ERROR_QUERY_FULLTEXT_INDEX_MISSING,1571,"no suitable fulltext index found for fulltext query on '%s'","Will be raised when a fulltext query is performed on a collection without a suitable fulltext index."
################################################################################
## AQL user functions
################################################################################
ERROR_QUERY_FUNCTION_INVALID_NAME,1580,"invalid user function name","Will be raised when a user function with an invalid name is registered."
ERROR_QUERY_FUNCTION_INVALID_CODE,1581,"invalid user function code","Will be raised when a user function is registered with invalid code."
ERROR_QUERY_FUNCTION_NOT_FOUND,1582,"user function not found","Will be raised when a user function is accessed but not found."
################################################################################
## ArangoDB cursor errors
################################################################################

View File

@ -106,6 +106,9 @@ void TRI_InitialiseErrorMessages (void) {
REG_ERROR(ERROR_QUERY_FAIL_CALLED, "FAIL(%s) called");
REG_ERROR(ERROR_QUERY_GEO_INDEX_MISSING, "no suitable geo index found for geo restriction on '%s'");
REG_ERROR(ERROR_QUERY_FULLTEXT_INDEX_MISSING, "no suitable fulltext index found for fulltext query on '%s'");
REG_ERROR(ERROR_QUERY_FUNCTION_INVALID_NAME, "invalid user function name");
REG_ERROR(ERROR_QUERY_FUNCTION_INVALID_CODE, "invalid user function code");
REG_ERROR(ERROR_QUERY_FUNCTION_NOT_FOUND, "user function not found");
REG_ERROR(ERROR_CURSOR_NOT_FOUND, "cursor not found");
REG_ERROR(ERROR_TRANSACTION_INCOMPLETE, "transaction definition is incomplete");
REG_ERROR(ERROR_TRANSACTION_INVALID_STATE, "invalid transaction state");

View File

@ -230,6 +230,12 @@ extern "C" {
/// - 1571: @LIT{no suitable fulltext index found for fulltext query on '\%s'}
/// Will be raised when a fulltext query is performed on a collection without
/// a suitable fulltext index.
/// - 1580: @LIT{invalid user function name}
/// Will be raised when a user function with an invalid name is registered.
/// - 1581: @LIT{invalid user function code}
/// Will be raised when a user function is registered with invalid code.
/// - 1582: @LIT{user function not found}
/// Will be raised when a user function is accessed but not found.
/// - 1600: @LIT{cursor not found}
/// Will be raised when a cursor is requested via its id but a cursor with
/// that id cannot be found.
@ -1372,6 +1378,36 @@ void TRI_InitialiseErrorMessages (void);
#define TRI_ERROR_QUERY_FULLTEXT_INDEX_MISSING (1571)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1580: ERROR_QUERY_FUNCTION_INVALID_NAME
///
/// invalid user function name
///
/// Will be raised when a user function with an invalid name is registered.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_QUERY_FUNCTION_INVALID_NAME (1580)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1581: ERROR_QUERY_FUNCTION_INVALID_CODE
///
/// invalid user function code
///
/// Will be raised when a user function is registered with invalid code.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_QUERY_FUNCTION_INVALID_CODE (1581)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1582: ERROR_QUERY_FUNCTION_NOT_FOUND
///
/// user function not found
///
/// Will be raised when a user function is accessed but not found.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_QUERY_FUNCTION_NOT_FOUND (1582)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1600: ERROR_CURSOR_NOT_FOUND
///