1
0
Fork 0

Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel

This commit is contained in:
Frank Celler 2013-04-05 13:25:04 +02:00
commit 71bf83be1b
38 changed files with 2108 additions and 952 deletions

View File

@ -222,7 +222,7 @@ A Foxx Model can be initialized with an object of attributes and their values.
The attributes property is the internal hash containing the model's state.
#### Foxx.Model#toJSON
@copydetails jsf_foxx_model_toJSON
@copydetails JSF_foxx_model_toJSON
## Foxx.Repository

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
////////////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,10 @@
/* A Bison parser, made by GNU Bison 2.5. */
/* Bison interface for Yacc-like parsers in C
/* A Bison parser, made by GNU Bison 2.4.1. */
/* Skeleton interface for Bison's Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -96,7 +98,7 @@
typedef union YYSTYPE
{
/* Line 2132 of yacc.c */
/* Line 1676 of yacc.c */
#line 26 "arangod/Ahuacatl/ahuacatl-grammar.y"
TRI_aql_node_t* node;
@ -106,8 +108,8 @@ typedef union YYSTYPE
/* Line 2132 of yacc.c */
#line 111 "arangod/Ahuacatl/ahuacatl-grammar.h"
/* Line 1676 of yacc.c */
#line 113 "arangod/Ahuacatl/ahuacatl-grammar.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */

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

@ -2254,17 +2254,17 @@ static v8::Handle<v8::Value> JS_PersistGeneralCursor (v8::Arguments const& argv)
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the next x rows from the cursor in one go
/// @brief return all following rows from the cursor in one go
///
/// This function constructs multiple rows at once and should be preferred over
/// hasNext()...next() when iterating over bigger result sets
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> JS_GetRowsGeneralCursor (v8::Arguments const& argv) {
static v8::Handle<v8::Value> JS_ToArrayGeneralCursor (v8::Arguments const& argv) {
v8::HandleScope scope;
if (argv.Length() != 0) {
TRI_V8_EXCEPTION_USAGE(scope, "getRows()");
TRI_V8_EXCEPTION_USAGE(scope, "toArray()");
}
TRI_vocbase_t* vocbase = GetContextVocBase();
@ -2319,6 +2319,15 @@ static v8::Handle<v8::Value> JS_GetRowsGeneralCursor (v8::Arguments const& argv)
TRI_V8_EXCEPTION(scope, TRI_ERROR_CURSOR_NOT_FOUND);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief alias for toArray()
/// @deprecated
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> JS_GetRowsGeneralCursor (v8::Arguments const& argv) {
return JS_ToArrayGeneralCursor(argv);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return max number of results per transfer for cursor
////////////////////////////////////////////////////////////////////////////////
@ -6658,6 +6667,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle<v8::Context> context,
TRI_AddProtoMethodVocbase(pt, "id", JS_IdGeneralCursor);
TRI_AddProtoMethodVocbase(pt, "next", JS_NextGeneralCursor);
TRI_AddProtoMethodVocbase(pt, "persist", JS_PersistGeneralCursor);
TRI_AddProtoMethodVocbase(pt, "toArray", JS_ToArrayGeneralCursor);
TRI_AddProtoMethodVocbase(pt, "unuse", JS_UnuseGeneralCursor);
rt = ft->InstanceTemplate();

View File

@ -220,7 +220,7 @@ static bool Compactifier (TRI_df_marker_t const* marker, void* data, TRI_datafil
TRI_document_collection_t* doc;
TRI_primary_collection_t* primary;
TRI_voc_fid_t fid;
TRI_voc_tid_t tid;
// TRI_voc_tid_t tid;
int res;
doc = data;
@ -232,7 +232,7 @@ static bool Compactifier (TRI_df_marker_t const* marker, void* data, TRI_datafil
TRI_doc_document_key_marker_t const* d = (TRI_doc_document_key_marker_t const*) marker;
bool deleted;
tid = d->_tid;
// tid = d->_tid;
// check if the document is still active
TRI_READ_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(primary);
@ -292,7 +292,7 @@ static bool Compactifier (TRI_df_marker_t const* marker, void* data, TRI_datafil
// deletion
else if (marker->_type == TRI_DOC_MARKER_KEY_DELETION) {
TRI_doc_deletion_key_marker_t const* d = (TRI_doc_deletion_key_marker_t const*) marker;
tid = d->_tid;
// tid = d->_tid;
// write to compactor files
res = CopyDocument(doc, marker, &result, &fid);

View File

@ -467,7 +467,10 @@ typedef struct TRI_doc_begin_transaction_marker_s {
TRI_df_marker_t base;
TRI_voc_tid_t _tid;
uint16_t _numCollections;
uint32_t _numCollections;
#ifdef TRI_PADDING_32
char _padding_begin_marker[4];
#endif
}
TRI_doc_begin_transaction_marker_t;

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

@ -164,6 +164,32 @@ function CollectionVolatileSuite () {
internal.wait(4);
assertEqual(true, c.properties().isVolatile);
assertEqual(0, c.count());
assertEqual([ ], c.toArray());
},
////////////////////////////////////////////////////////////////////////////////
/// @brief compaction
////////////////////////////////////////////////////////////////////////////////
testCompaction : function () {
var c = internal.db._create(cn, { isVolatile : true, journalSize: 1024 * 1024 });
assertEqual(true, c.properties().isVolatile);
for (var i = 0; i < 10000; ++i) {
c.save({"test": "the quick brown fox jumped over the lazy dog", "foo" : "bar"});
}
assertEqual(10000, c.count());
c.truncate();
c.save({"test": 1});
assertEqual(1, c.count());
c.truncate();
c.unload();
internal.wait(5);
assertEqual(0, c.count());
assertEqual([ ], c.toArray());
}
};

View File

@ -191,6 +191,22 @@
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief reloads the AQL user functions
////////////////////////////////////////////////////////////////////////////////
if (typeof SYS_EXECUTE_GLOBAL_CONTEXT_FUNCTION === "undefined") {
internal.reloadAqlFunctions = function () {
require("org/arangodb/ahuacatl").reload();
};
}
else {
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

@ -2022,7 +2022,10 @@ exports.HTTP_NOT_FOUND = 404;
exports.HTTP_METHOD_NOT_ALLOWED = 405;
exports.HTTP_CONFLICT = 409;
exports.HTTP_PRECONDITION_FAILED = 412;
exports.HTTP_ENTITY_TOO_LARGE = 413;
exports.HTTP_I_AM_A_TEAPOT = 418;
exports.HTTP_UNPROCESSABLE_ENTIT = 422;
exports.HTTP_HEADER_TOO_LARGE = 431;
// HTTP 5xx
exports.HTTP_SERVER_ERROR = 500;

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

@ -59,7 +59,10 @@ var Application,
/// a constraint and a method. For example:
///
/// @EXAMPLES
///
/// @code
/// internal.createUrlObject('/lecker/gans', null, 'get');
/// @endcode
////////////////////////////////////////////////////////////////////////////////
internal.createUrlObject = function (url, constraint, method) {
@ -89,10 +92,13 @@ internal.createUrlObject = function (url, constraint, method) {
/// * **The Template Collection:** More information in the template section
///
/// @EXAMPLES
///
/// @code
/// app = new Application({
/// urlPrefix: "/wiese",
/// templateCollection: "my_templates"
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
Application = function (options) {
@ -170,9 +176,12 @@ _.extend(Application.prototype, {
/// object).
///
/// @EXAMPLES
///
/// @code
/// app.handleRequest("get", "/gaense", function (req, res) {
/// //handle the request
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
handleRequest: function (method, route, callback) {
@ -216,9 +225,12 @@ _.extend(Application.prototype, {
/// See above for the arguments you can give. An example:
///
/// @EXAMPLES
///
/// @code
/// app.get('/gaense/stall', function (req, res) {
/// // Take this request and deal with it!
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
get: function (route, callback) {
'use strict';
@ -233,9 +245,12 @@ _.extend(Application.prototype, {
/// See above for the arguments you can give. An example:
///
/// @EXAMPLES
///
/// @code
/// app.post('/gaense/stall', function (req, res) {
/// // Take this request and deal with it!
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
post: function (route, callback) {
'use strict';
@ -250,9 +265,12 @@ _.extend(Application.prototype, {
/// See above for the arguments you can give. An example:
///
/// @EXAMPLES
///
/// @code
/// app.put('/gaense/stall', function (req, res) {
/// // Take this request and deal with it!
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
put: function (route, callback) {
'use strict';
@ -267,9 +285,12 @@ _.extend(Application.prototype, {
/// See above for the arguments you can give. An example:
///
/// @EXAMPLES
///
/// @code
/// app.patch('/gaense/stall', function (req, res) {
/// // Take this request and deal with it!
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
patch: function (route, callback) {
'use strict';
@ -288,6 +309,8 @@ _.extend(Application.prototype, {
/// for this very reason.
///
/// @EXAMPLES
///
/// @code
/// app['delete']('/gaense/stall', function (req, res) {
/// // Take this request and deal with it!
/// });
@ -295,6 +318,7 @@ _.extend(Application.prototype, {
/// app.del('/gaense/stall', function (req, res) {
/// // Take this request and deal with it!
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
'delete': function (route, callback) {
@ -319,9 +343,12 @@ _.extend(Application.prototype, {
/// An example:
///
/// @EXAMPLES
///
/// @code
/// app.before('/high/way', function(req, res) {
/// //Do some crazy request logging
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
before: function (path, func) {
@ -350,9 +377,12 @@ _.extend(Application.prototype, {
/// An example:
///
/// @EXAMPLES
///
/// @code
/// app.after('/high/way', function(req, res) {
/// //Do some crazy response logging
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
after: function (path, func) {
@ -381,9 +411,12 @@ _.extend(Application.prototype, {
/// blocks.
///
/// @EXAMPLES
///
/// @code
/// app.helper("link_to", function (identifier) {
/// return urlRepository[identifier];
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
helper: function (name, func) {
@ -400,7 +433,10 @@ _.extend(Application.prototype, {
/// This is a shortcut to add the middleware to your application:
///
/// @EXAMPLES
///
/// @code
/// app.accepts(["json"], "json");
/// @endcode
////////////////////////////////////////////////////////////////////////////////
accepts: function (allowedFormats, defaultFormat) {
@ -446,12 +482,15 @@ _.extend(RequestContext.prototype, {
/// You can also provide a description of this parameter.
///
/// @EXAMPLES
///
/// @code
/// app.get("/foxx/:id", function {
/// // Do something
/// }).pathParam("id", {
/// description: "Id of the Foxx",
/// dataType: "int"
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
pathParam: function (paramName, attributes) {
@ -489,6 +528,8 @@ _.extend(RequestContext.prototype, {
/// if you can provide the parameter multiple times.
///
/// @EXAMPLES
///
/// @code
/// app.get("/foxx", function {
/// // Do something
/// }).queryParam("id", {
@ -497,6 +538,7 @@ _.extend(RequestContext.prototype, {
/// required: true,
/// allowMultiple: false
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
queryParam: function (paramName, attributes) {
@ -632,7 +674,10 @@ BaseMiddleware = function (templateCollection, helperCollection) {
/// Set the status code of your response, for example:
///
/// @EXAMPLES
/// response.status(404);
///
/// @code
/// response.status(404);
/// @endcode
////////////////////////////////////////////////////////////////////////////////
status: function (code) {
this.responseCode = code;
@ -645,6 +690,8 @@ BaseMiddleware = function (templateCollection, helperCollection) {
/// Set a header attribute, for example:
///
/// @EXAMPLES
///
/// @code
/// response.set("Content-Length", 123);
/// response.set("Content-Type", "text/plain");
///
@ -654,6 +701,7 @@ BaseMiddleware = function (templateCollection, helperCollection) {
/// "Content-Length": "123",
/// "Content-Type": "text/plain"
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
set: function (key, value) {
@ -683,7 +731,10 @@ BaseMiddleware = function (templateCollection, helperCollection) {
/// JSON encoded object you provided.
///
/// @EXAMPLES
///
/// @code
/// response.json({'born': 'December 12, 1915'});
/// @endcode
////////////////////////////////////////////////////////////////////////////////
json: function (obj) {
@ -725,7 +776,10 @@ BaseMiddleware = function (templateCollection, helperCollection) {
/// ViewHelper section.
///
/// @EXAMPLES
///
/// @code
/// response.render("high/way", {username: 'Application'})
/// @endcode
////////////////////////////////////////////////////////////////////////////////
render: function (templatePath, data) {
var template;
@ -853,9 +907,12 @@ FormatMiddleware = function (allowedFormats, defaultFormat) {
/// as an object.
///
/// @EXAMPLES
///
/// @code
/// instance = new Model({
/// a: 1
/// });
/// @endcode
////////////////////////////////////////////////////////////////////////////////
Model = function (attributes) {
@ -871,11 +928,14 @@ _.extend(Model.prototype, {
///
/// Get the value of an attribute
/// @EXAMPLES
///
/// @code
/// instance = new Model({
/// a: 1
/// });
///
/// instance.get("a");
/// @endcode
////////////////////////////////////////////////////////////////////////////////
get: function (attributeName) {
@ -888,11 +948,14 @@ _.extend(Model.prototype, {
///
/// Set the value of an attribute
/// @EXAMPLES
///
/// @code
/// instance = new Model({
/// a: 1
/// });
///
/// instance.set("a", 2);
/// @endcode
////////////////////////////////////////////////////////////////////////////////
set: function (attributeName, value) {
@ -905,12 +968,15 @@ _.extend(Model.prototype, {
///
/// Returns true if the attribute is set to a non-null or non-undefined value.
/// @EXAMPLES
///
/// @code
/// instance = new Model({
/// a: 1
/// });
///
/// instance.has("a"); //=> true
/// instance.has("b"); //=> false
/// @endcode
////////////////////////////////////////////////////////////////////////////////
has: function (attributeName) {
@ -949,7 +1015,10 @@ Model.extend = backbone_helpers.extend;
/// If you initialize a model, you can give it initial data as an object.
///
/// @EXAMPLES
///
/// @code
/// instance = new Repository(prefix, collection, modelPrototype);
/// @endcode
////////////////////////////////////////////////////////////////////////////////
Repository = function (prefix, collection, modelPrototype) {

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
///

View File

@ -51,7 +51,6 @@ using namespace std;
string HttpResponse::responseString (HttpResponseCode code) {
switch (code) {
// Success 2xx
case OK: return "200 OK";
case CREATED: return "201 Created";
@ -77,6 +76,7 @@ string HttpResponse::responseString (HttpResponseCode code) {
case LENGTH_REQUIRED: return "411 Length Required";
case PRECONDITION_FAILED: return "412 Precondition Failed";
case ENTITY_TOO_LARGE: return "413 Request Entity Too Large";
case I_AM_A_TEAPOT: return "418 I'm a teapot";
case UNPROCESSABLE_ENTITY: return "422 Unprocessable Entity";
case HEADER_TOO_LARGE: return "431 Request Header Fields Too Large";
@ -120,6 +120,7 @@ HttpResponse::HttpResponseCode HttpResponse::responseCode (const string& str) {
case 405: return METHOD_NOT_ALLOWED;
case 409: return CONFLICT;
case 412: return PRECONDITION_FAILED;
case 418: return I_AM_A_TEAPOT;
case 422: return UNPROCESSABLE_ENTITY;
case 500: return SERVER_ERROR;

View File

@ -100,6 +100,7 @@ namespace triagens {
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
ENTITY_TOO_LARGE = 413,
I_AM_A_TEAPOT = 418,
UNPROCESSABLE_ENTITY = 422,
HEADER_TOO_LARGE = 431,