1
0
Fork 0

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

This commit is contained in:
Michael Hackstein 2013-03-21 10:55:12 +01:00
commit 3c2f88d6ab
40 changed files with 2273 additions and 1046 deletions

137
CHANGELOG
View File

@ -1,6 +1,29 @@
v1.3 (XXXX-XX-XX)
-----------------
* issue #457: invalid string value in web interface
* make datafile id (datafile->_fid) identical to the numeric part of the filename.
E.g. the datafile `journal-123456.db` will now have a datafile marker with the same
fid (i.e. `123456`) instead of a different value. This change will only affect
datafiles that are created with 1.3 and not any older files.
The intention behind this change is to make datafile debugging easier.
* consistently discard document attributes with reserved names (system attributes)
but without any known meaning, for example `_test`, `_foo`, ...
Previously, these attributes were saved with the document regularly in some cases,
but were discarded in other cases.
Now these attributes are discarded consistently. "Real" system attributes such as
`_key`, `_from`, `_to` are not affected and will work as before.
Additionally, attributes with an empty name (``) are discarded when documents are
saved.
Though using reserved or empty attribute names in documents was not really and
consistently supported in previous versions of ArangoDB, this change might cause
an incompatibility for clients that rely on this feature.
* added server startup flag `--database.force-sync-properties` to force syncing of
collection properties on collection creation, deletion and on property update.
The default value is true to mimic the behavior of previous versions of ArangoDB.
@ -13,55 +36,6 @@ v1.3 (XXXX-XX-XX)
* issue #443: For git-based builds include commit hash in version
* issue #212: auto-increment support
The feature can be used by creating a collection with the extra `keyOptions`
attribute as follows:
db._create("mycollection", { keyOptions: { type: "autoincrement", offset: 1, increment: 10, allowUserKeys: true } });
The `type` attribute will make sure the keys will be auto-generated if no
`_key` attribute is specified for a document.
The `allowUserKeys` attribute determines whether users might still supply own
`_key` values with documents or if this is considered an error.
The `increment` value determines the actual increment value, whereas the `offset`
value can be used to seed to value sequence with a specific starting value.
This will be useful later in a multi-master setup, when multiple servers can use
different auto-increment seed values and thus generate non-conflicting auto-increment values.
The default values currently are:
- `allowUserKeys`: `true`
- `offset`: `0`
- `increment`: `1`
The only other available key generator type currently is `traditional`.
The `traditional` key generator will auto-generate keys in a fashion as ArangoDB
always did (some increasing integer value, with a more or less unpredictable
increment value).
Note that for the `traditional` key generator there is no the option to disallow
user-supplied keys and give the server the sole responsibility for key generation.
This also introduces the following errors that API implementors may want to check
the return values for:
- 1222: `document key unexpected`: will be raised when a document is created with
a `_key` attribute, but the underlying collection was set up with the key generator
attribute `allowUserKeys: false`.
- 1225: `out of keys`: will be raised when the auto-increment key generator runs
out of keys. This may happen when the next key to be generated is 2^64 or higher.
In practice, this will only happen if the values for `increment` or `offset` are
not set appropriately, or if users are allowed to supply own keys, those keys
are near the 2^64 threshold, and later the auto-increment feature kicks in and
generates keys that cross that threshold.
It will not occur in practice with proper configuration and proper usage of the
collections.
* adjust startup log output to be more compact, less verbose
* set the required minimum number of file descriptors to 256.
@ -112,6 +86,71 @@ v1.3 (XXXX-XX-XX)
v1.2.2 (XXXX-XX-XX)
-------------------
* issue #212: auto-increment support
The feature can be used by creating a collection with the extra `keyOptions`
attribute as follows:
db._create("mycollection", { keyOptions: { type: "autoincrement", offset: 1, increment: 10, allowUserKeys: true } });
The `type` attribute will make sure the keys will be auto-generated if no
`_key` attribute is specified for a document.
The `allowUserKeys` attribute determines whether users might still supply own
`_key` values with documents or if this is considered an error.
The `increment` value determines the actual increment value, whereas the `offset`
value can be used to seed to value sequence with a specific starting value.
This will be useful later in a multi-master setup, when multiple servers can use
different auto-increment seed values and thus generate non-conflicting auto-increment values.
The default values currently are:
- `allowUserKeys`: `true`
- `offset`: `0`
- `increment`: `1`
The only other available key generator type currently is `traditional`.
The `traditional` key generator will auto-generate keys in a fashion as ArangoDB
always did (some increasing integer value, with a more or less unpredictable
increment value).
Note that for the `traditional` key generator there is only the option to disallow
user-supplied keys and give the server the sole responsibility for key generation.
This can be achieved by setting the `allowUserKeys` property to `false`.
This change also introduces the following errors that API implementors may want to check
the return values for:
- 1222: `document key unexpected`: will be raised when a document is created with
a `_key` attribute, but the underlying collection was set up with the `keyOptions`
attribute `allowUserKeys: false`.
- 1225: `out of keys`: will be raised when the auto-increment key generator runs
out of keys. This may happen when the next key to be generated is 2^64 or higher.
In practice, this will only happen if the values for `increment` or `offset` are
not set appropriately, or if users are allowed to supply own keys, those keys
are near the 2^64 threshold, and later the auto-increment feature kicks in and
generates keys that cross that threshold.
In practice it should not occur with proper configuration and proper usage of the
collections.
This change may also affect the following REST APIs:
- POST `/_api/collection`: the server does now accept the optional `keyOptions`
attribute in the second parameter
- GET `/_api/collection/properties`: will return the `keyOptions` attribute as part
of the collection's properties. The previous optional attribute `createOptions`
is now gone.
* fixed `ArangoStatement.explain()` method with bind variables
* fixed misleading "cursor not found" error message in arangosh that occurred when
`count()` was called for client-side cursors
* fixed handling of empty attribute names, which may have crashed the server under
certain circumstances before
* fixed usage of invalid pointer in error message output when index description could
not be opened

View File

@ -31,7 +31,9 @@ Users can define their own keys for documents they save. The document key will
be saved along with a document in the `_key` attribute. Users can pick key
values as required, provided that the values conform to the following
restrictions:
* the key must be at most 254 bytes long
* the key must be at least 1 byte and at most 254 bytes long. empty keys are
disallowed when specified (though it may be valid to completely omit the
`_key` attribute from a document)
* it must consist of the letters a-z (lower or upper case), the digits 0-9,
the underscore (_), dash (-), or colon (:) characters only
* any other characters, especially multi-byte sequences, whitespace or
@ -63,6 +65,7 @@ following attribute naming constraints are not violated:
More system attributes may be added in the future without further notice so
end users should not use attribute names starting with an underscore for their
own attributes.
- Attribute names should not start with the at-mark (`\@`). The at-mark
at the start of attribute names is reserved in ArangoDB for future use cases.
- Theoretically, attribute names can include punctuation and special characters
@ -84,3 +87,17 @@ following attribute naming constraints are not violated:
length is variable and depends on the number and data types of attributes
used.
- Attribute names are case-sensitive.
- Attributes with empty names (the empty string) and attributes with names that
start with an underscore and don't have a special meaning (system attributes)
are removed from the document when saving it.
When the document is later requested, it will be returned without these
attributes. For example, if this document is saved
{ "a" : 1, "" : 2, "_test" : 3, "b": 4 }
and later requested, it will be returned like this:
{ "a" : 1, "b": 4 }

View File

@ -0,0 +1,105 @@
# Fuxx: Build APIs and simple web applications in ArangoDB
**WARNING: The following documentation file is pure fiction,
it is not yet finished**
An application build with Fuxx is written in JavaScript and deployed
to ArangoDB directly. ArangoDB serves this application, you do not
need a separate application server.
So given you want to build an application that sends a plain-text
response "Worked!" for all requests to `/my/wiese`. How would you
achieve that with Fuxx?
First, create a directory `my_app` and save a file called `app.js`
in this directory. Write the following content to this file:
FuxxApplication = require("org/arangodb/fuxx").FuxxApplication;
app = new FuxxApplication();
app.get('/wiese', function(req, res) {
res.set("Content-Type", "text/plain");
res.body = "Worked!"
});
app.start();
This is your application. Now we need to mount it to the path `/my`.
In order to achieve that, we create a file called `manifest.json` in
our `my_app` directory with the following content:
{
'apps': {
'/my': 'app.js'
}
}
Now your application is done. Start ArangoDB as follows:
arangod --app my_app /tmp/fancy_db
Now point your browser to `/my/wiese` and you should see "Worked!".
After this short overview, let's get into the details.
## FuxxApplication Features
Please see the documentation of `fuxx.js` for further information on how to write the application file.
## Manifest Files
When you start arangod with the `--app` option, ArangoDB scans the
given directory on every request for files called `manifest.json`.
There can be a file in the root directory and in each direct subdirectory if you want that.
The content is a JSON object with two keys: `apps` and `libs`.
(*we will also add a third one called `vendor` for NPM packages, but
this does not exist yet*).
`apps` is an object that matches routes to files:
* The `key` is the route you want to mount at
* The `value` is the path to the JavaScript file containing the `FuxxApplication`s you want to mount
You can add multiple applications in one manifest in this way.
In addition you can add an optional `lib` String. This is a path to
a folder containing multiple JavaScript files which define CommonJS
modules that you want to use in your Fuxx apps. They will all be loaded,
so you can require them as usual. The `lib` folder can be structured however
you want. If you have a folder `models` in your `lib` folder containing
a file `user.js`, you can require it with `user = require('models/user')`.
A more complete example for a Manifest file:
{
'apps': {
'/blog': 'apps/blog.js',
'/shop': 'apps/shop.js'
},
'lib': 'lib'
}
## Development Mode
If you start the application in the way described above, you are working
in development mode. This means that on every request:
1. All routes are dropped
2. All module caches are flushed
3. Your app directory is scanned for manifest files, and each manifest file is read
4. All files in your lib folder(s) are loaded
5. All `apps` are executed. Each `app.start()` will put the routes in a temporary route object, prefixed with the path given in the manifest
6. All routes in the temporary route object are stored to the routes
7. The request will be processed
This means that you do not have to restart ArangoDB if you change anything in your app
(*This will change when we add support for `vendor`, they will not be reloaded*).
It is of course not meant for production, because the reloading makes the app relatively slow.
## Deploying on Production
If you are comfortable to deploy your app to your production ArangoDB, you will have to do
the following:
**Not yet decided**

View File

@ -0,0 +1,163 @@
# coding: utf-8
require 'rspec'
require './arangodb.rb'
describe ArangoDB do
api = "/_api/document"
prefix = "attributes"
context "dealing with attribute names" do
before do
@cn = "UnitTestsCollectionAttributes"
ArangoDB.drop_collection(@cn)
@cid = ArangoDB.create_collection(@cn)
end
after do
ArangoDB.drop_collection(@cn)
end
################################################################################
## creates a document with an empty attribute
################################################################################
it "creates a document with an empty attribute name" do
cmd = api + "?collection=" + @cn
body = "{ \"\" : \"a\", \"foo\" : \"b\" }"
doc = ArangoDB.log_post("#{prefix}-create-empty-name", cmd, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
id = doc.parsed_response['_id']
cmd = api + "/" + id
doc = ArangoDB.log_get("#{prefix}-create-empty-name", cmd)
doc.parsed_response.should_not have_key('')
doc.parsed_response.should have_key('foo')
end
################################################################################
## queries a document with an empty attribute
################################################################################
it "queries a document with an empty attribute name" do
cmd = api + "?collection=" + @cn
body = "{ \"\" : \"a\", \"foo\" : \"b\" }"
doc = ArangoDB.log_post("#{prefix}-query-empty-name", cmd, :body => body)
doc.code.should eq(201)
doc.parsed_response['error'].should eq(false)
cmd = "/_api/simple/all"
body = "{ \"collection\" : \"" + @cn + "\" }"
doc = ArangoDB.log_put("#{prefix}-query-empty-name", cmd, :body => body)
documents = doc.parsed_response['result']
documents.length.should eq(1)
documents[0].should_not have_key('')
documents[0].should have_key('foo')
end
################################################################################
## creates a document with reserved attribute names
################################################################################
it "creates a document with reserved attribute names" do
cmd = api + "?collection=" + @cn
body = "{ \"_rev\" : \"99\", \"foo\" : \"002\", \"_id\" : \"meow\", \"_from\" : \"a\", \"_to\" : \"b\", \"_test\" : \"c\", \"meow\" : \"d\" }"
doc = ArangoDB.log_post("#{prefix}-create-reserved-names", cmd, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
id = doc.parsed_response['_id']
cmd = api + "/" + id
doc = ArangoDB.log_get("#{prefix}-create-reserved-names", cmd)
doc.parsed_response['_id'].should eq(id)
doc.parsed_response['_rev'].should_not eq('99')
doc.parsed_response.should_not have_key('_from')
doc.parsed_response.should_not have_key('_to')
doc.parsed_response.should_not have_key('_test')
doc.parsed_response.should have_key('meow')
doc.parsed_response['meow'].should eq('d')
doc.parsed_response['foo'].should eq('002')
end
################################################################################
## nested attribute names
################################################################################
it "creates a document with nested attribute names" do
cmd = api + "?collection=" + @cn
body = "{ \"a\" : \"1\", \"b\" : { \"b\" : \"2\" , \"a\" : \"3\", \"\": \"4\", \"_from\": \"5\", \"c\" : 6 } }"
doc = ArangoDB.log_post("#{prefix}-create-duplicate-names", cmd, :body => body)
doc.code.should eq(201)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(false)
id = doc.parsed_response['_id']
cmd = api + "/" + id
doc = ArangoDB.log_get("#{prefix}-create-empty-name", cmd)
doc.parsed_response.should have_key('a')
doc.parsed_response['a'].should eq('1')
doc.parsed_response.should have_key('b')
doc.parsed_response['b'].should_not have_key('')
doc.parsed_response['b'].should_not have_key('_from')
doc.parsed_response['b'].should have_key('b')
doc.parsed_response['b'].should have_key('a')
doc.parsed_response['b'].should have_key('c')
doc.parsed_response['b'].should eq({ "b" => "2", "a" => "3", "c" => 6 })
end
################################################################################
## duplicate attribute names
################################################################################
it "creates a document with duplicate attribute names" do
cmd = api + "?collection=" + @cn
body = "{ \"a\" : \"1\", \"b\" : \"2\", \"a\" : \"3\" }"
doc = ArangoDB.log_post("#{prefix}-create-duplicate-names", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(600)
end
################################################################################
## nested duplicate attribute names
################################################################################
it "creates a document with nested duplicate attribute names" do
cmd = api + "?collection=" + @cn
body = "{ \"a\" : \"1\", \"b\" : { \"b\" : \"2\" , \"c\" : \"3\", \"b\": \"4\" } }"
doc = ArangoDB.log_post("#{prefix}-create-duplicate-names-nested", cmd, :body => body)
doc.code.should eq(400)
doc.headers['content-type'].should eq("application/json; charset=utf-8")
doc.parsed_response['error'].should eq(true)
doc.parsed_response['code'].should eq(400)
doc.parsed_response['errorNum'].should eq(600)
end
end
end

View File

@ -4,6 +4,7 @@ test -d logs || mkdir logs
rspec --color --format d \
api-http-spec.rb \
api-admin-spec.rb \
api-attributes-spec.rb \
api-batch-spec.rb \
api-collection-spec.rb \
api-graph-spec.rb \

View File

@ -210,6 +210,7 @@ endif
################################################################################
SHELL_COMMON = @top_srcdir@/js/common/tests/shell-require.js \
@top_srcdir@/js/common/tests/shell-attributes.js \
@top_srcdir@/js/common/tests/shell-document.js \
@top_srcdir@/js/common/tests/shell-edge.js \
@top_srcdir@/js/common/tests/shell-database.js \
@ -231,7 +232,7 @@ SHELL_COMMON = @top_srcdir@/js/common/tests/shell-require.js \
SHELL_SERVER = $(SHELL_COMMON) \
@top_srcdir@/js/server/tests/routing.js \
@top_srcdir@/js/common/tests/shell-frank.js \
@top_srcdir@/js/common/tests/shell-fuxx.js \
@top_srcdir@/js/common/tests/shell-graph-traversal.js \
@top_srcdir@/js/common/tests/shell-graph-algorithms.js \
@top_srcdir@/js/common/tests/shell-graph-measurement.js \

View File

@ -28,12 +28,6 @@
#ifndef TRIAGENS_UTILS_EMBEDDABLE_TRANSACTION_H
#define TRIAGENS_UTILS_EMBEDDABLE_TRANSACTION_H 1
#include "VocBase/transaction.h"
#include "V8/v8-globals.h"
#include <v8.h>
namespace triagens {
namespace arango {
@ -59,14 +53,8 @@ namespace triagens {
/// @brief create the transaction
////////////////////////////////////////////////////////////////////////////////
EmbeddableTransaction () : _trx(0) {
TRI_v8_global_t* v8g;
v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
if (v8g->_currentTransaction == 0) {
_previous = (TRI_transaction_t*) v8g->_currentTransaction;
}
EmbeddableTransaction () :
C() {
}
////////////////////////////////////////////////////////////////////////////////
@ -99,52 +87,6 @@ namespace triagens {
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free the transaction
////////////////////////////////////////////////////////////////////////////////
int freeTransaction () {
if (this->isEmbedded()) {
return TRI_ERROR_NO_ERROR;
}
if (_trx != 0) {
this->unregisterTransaction();
TRI_FreeTransaction(_trx);
_trx = 0;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief previous transaction, if any
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* _previous;
////////////////////////////////////////////////////////////////////////////////
/// @brief used transaction
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* _trx;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -80,19 +80,10 @@ namespace triagens {
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the transaction is embedded
/// @brief return the parent transaction (none in our case)
////////////////////////////////////////////////////////////////////////////////
inline bool isEmbedded () const {
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the parent transaction if any
////////////////////////////////////////////////////////////////////////////////
inline TRI_transaction_t* getParent () const {
assert(false);
inline TRI_transaction_t* getParentTransaction () const {
return 0;
}

View File

@ -109,7 +109,7 @@ namespace triagens {
inline TRI_primary_collection_t* primaryCollection () {
assert(this->_cid > 0);
TRI_vocbase_col_t* collection = TRI_CheckCollectionTransaction(this->_trx, this->_cid, TRI_TRANSACTION_READ);
TRI_vocbase_col_t* collection = TRI_CheckCollectionTransaction(this->getTrx(), this->_cid, TRI_TRANSACTION_READ);
assert(collection != 0);
assert(collection->_collection != 0);

View File

@ -28,8 +28,6 @@
#ifndef TRIAGENS_UTILS_STANDALONE_TRANSACTION_H
#define TRIAGENS_UTILS_STANDALONE_TRANSACTION_H 1
#include "VocBase/transaction.h"
namespace triagens {
namespace arango {
@ -56,8 +54,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
StandaloneTransaction () :
C(),
_trx(0) {
C() {
}
////////////////////////////////////////////////////////////////////////////////
@ -90,27 +87,6 @@ namespace triagens {
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief used transaction
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* _trx;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -87,18 +87,18 @@ namespace triagens {
Transaction (TRI_vocbase_t* const vocbase,
const triagens::arango::CollectionNameResolver& resolver) :
T(),
_vocbase(vocbase),
_resolver(resolver),
_trx(0),
_setupError(TRI_ERROR_NO_ERROR),
_nestingLevel(0),
_readOnly(true),
_hints(0) {
_hints(0),
_vocbase(vocbase),
_resolver(resolver) {
assert(_vocbase != 0);
TRI_ASSERT_DEBUG(_vocbase != 0);
int res = createTransaction();
if (res != TRI_ERROR_NO_ERROR) {
_setupError = res;
}
// normally this will return TRI_ERROR_NO_ERROR
this->_setupError = this->setupTransaction();
}
////////////////////////////////////////////////////////////////////////////////
@ -106,12 +106,15 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
virtual ~Transaction () {
if (this->_trx != 0 && ! this->isEmbedded()) {
if (this->status() == TRI_TRANSACTION_RUNNING) {
// auto abort
this->abort();
}
freeTransaction();
if (_trx == 0) {
return;
}
if (isEmbeddedTransaction()) {
destroyEmbedded();
}
else {
destroyToplevel();
}
}
@ -131,119 +134,11 @@ namespace triagens {
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief begin the transaction
/// @brief whether or not the transaction is embedded
////////////////////////////////////////////////////////////////////////////////
int begin () {
if (this->_trx == 0) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (this->isEmbedded()) {
if (this->status() == TRI_TRANSACTION_RUNNING) {
if (this->_setupError != TRI_ERROR_NO_ERROR) {
return this->_setupError;
}
return TRI_ERROR_NO_ERROR;
}
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (this->_setupError != TRI_ERROR_NO_ERROR) {
return this->_setupError;
}
if (this->status() != TRI_TRANSACTION_CREATED) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
return TRI_StartTransaction(this->_trx, _hints);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief commit / finish the transaction
////////////////////////////////////////////////////////////////////////////////
int commit () {
if (this->_trx == 0 || this->status() != TRI_TRANSACTION_RUNNING) {
// transaction not created or not running
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (this->isEmbedded()) {
// return instantly if the transaction is embedded
return TRI_ERROR_NO_ERROR;
}
int res;
if (this->_trx->_type == TRI_TRANSACTION_READ) {
// a read transaction just finishes
res = TRI_FinishTransaction(this->_trx);
}
else {
// a write transaction commits
res = TRI_CommitTransaction(this->_trx);
}
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief abort the transaction
////////////////////////////////////////////////////////////////////////////////
int abort () {
if (this->_trx == 0 || this->status() != TRI_TRANSACTION_RUNNING) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (this->isEmbedded() && this->isReadOnlyTransaction()) {
// return instantly if the transaction is embedded
return TRI_ERROR_NO_ERROR;
}
int res = TRI_AbortTransaction(this->_trx);
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief finish a transaction, based on the previous state
////////////////////////////////////////////////////////////////////////////////
int finish (const int errorNumber) {
if (this->_trx == 0 || this->status() != TRI_TRANSACTION_RUNNING) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
int res;
if (errorNumber == TRI_ERROR_NO_ERROR) {
// there was no previous error, so we'll commit
res = this->commit();
}
else {
// there was a previous error, so we'll abort
this->abort();
// return original error number
res = errorNumber;
}
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the status of the transaction
////////////////////////////////////////////////////////////////////////////////
inline TRI_transaction_status_e status () const {
if (this->_trx != 0) {
return this->_trx->_status;
}
return TRI_TRANSACTION_UNDEFINED;
inline bool isEmbeddedTransaction () const {
return (_nestingLevel > 0);
}
////////////////////////////////////////////////////////////////////////////////
@ -251,7 +146,99 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
inline bool isReadOnlyTransaction () const {
return _readOnly;
return this->_readOnly;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the status of the transaction
////////////////////////////////////////////////////////////////////////////////
inline TRI_transaction_status_e getStatus () const {
if (_trx != 0) {
return _trx->_status;
}
return TRI_TRANSACTION_UNDEFINED;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief begin the transaction
////////////////////////////////////////////////////////////////////////////////
int begin () {
if (_trx == 0) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (_setupError != TRI_ERROR_NO_ERROR) {
return _setupError;
}
LOGGER_TRACE("beginning transaction");
if (isEmbeddedTransaction()) {
return beginEmbedded();
}
else {
return beginToplevel();
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief commit / finish the transaction
////////////////////////////////////////////////////////////////////////////////
int commit () {
if (_trx == 0 || getStatus() != TRI_TRANSACTION_RUNNING) {
// transaction not created or not running
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
LOGGER_TRACE("committing transaction");
if (isEmbeddedTransaction()) {
return commitEmbedded();
}
else {
return commitToplevel();
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief abort the transaction
////////////////////////////////////////////////////////////////////////////////
int abort () {
if (_trx == 0 || getStatus() != TRI_TRANSACTION_RUNNING) {
// transaction not created or not running
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
LOGGER_TRACE("aborting transaction");
if (isEmbeddedTransaction()) {
return abortEmbedded();
}
else {
return abortToplevel();
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief finish a transaction (commit or abort), based on the previous state
////////////////////////////////////////////////////////////////////////////////
int finish (const int errorNumber) {
if (errorNumber == TRI_ERROR_NO_ERROR) {
// there was no previous error, so we'll commit
return this->commit();
}
// there was a previous error, so we'll abort
this->abort();
// return original error number
return errorNumber;
}
////////////////////////////////////////////////////////////////////////////////
@ -259,8 +246,8 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
void dump () {
if (this->_trx != 0) {
TRI_DumpTransaction(this->_trx);
if (_trx != 0) {
TRI_DumpTransaction(this->__trx);
}
}
@ -285,35 +272,37 @@ namespace triagens {
int addCollection (TRI_transaction_cid_t cid,
TRI_transaction_type_e type) {
if (this->_trx == 0) {
if (_trx == 0) {
return TRI_ERROR_INTERNAL;
}
if ((this->status() == TRI_TRANSACTION_RUNNING && ! this->isEmbedded()) ||
this->status() == TRI_TRANSACTION_COMMITTED ||
this->status() == TRI_TRANSACTION_ABORTED ||
this->status() == TRI_TRANSACTION_FINISHED) {
const TRI_transaction_status_e status = getStatus();
if ((status == TRI_TRANSACTION_RUNNING && ! this->isEmbeddedTransaction()) ||
status == TRI_TRANSACTION_COMMITTED ||
status == TRI_TRANSACTION_ABORTED ||
status == TRI_TRANSACTION_FINISHED) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (cid == 0) {
// invalid cid
return _setupError = TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
return this->_setupError = TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
}
if (type == TRI_TRANSACTION_WRITE) {
_readOnly = false;
this->_readOnly = false;
}
if (this->isEmbedded()) {
TRI_vocbase_col_t* collection = TRI_CheckCollectionTransaction(this->_trx, cid, type);
if (this->isEmbeddedTransaction()) {
TRI_vocbase_col_t* collection = TRI_CheckCollectionTransaction(_trx, cid, type);
if (collection == 0) {
// adding an unknown collection...
int res = TRI_ERROR_NO_ERROR;
if (type == TRI_TRANSACTION_READ && this->isReadOnlyTransaction()) {
res = TRI_AddDelayedReadCollectionTransaction(this->_trx, cid);
res = TRI_AddDelayedReadCollectionTransaction(_trx, cid);
}
else {
res = TRI_ERROR_TRANSACTION_UNREGISTERED_COLLECTION;
@ -327,7 +316,7 @@ namespace triagens {
return TRI_ERROR_NO_ERROR;
}
int res = TRI_AddCollectionTransaction(this->_trx, cid, type);
int res = TRI_AddCollectionTransaction(_trx, cid, type);
if (res != TRI_ERROR_NO_ERROR) {
_setupError = res;
@ -365,20 +354,20 @@ namespace triagens {
int lockExplicit (TRI_primary_collection_t* const primary,
const TRI_transaction_type_e type) {
if (this->_trx == 0) {
if (_trx == 0) {
return TRI_ERROR_INTERNAL;
}
if (this->status() != TRI_TRANSACTION_RUNNING) {
if (getStatus() != TRI_TRANSACTION_RUNNING) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (this->isEmbedded()) {
if (this->isEmbeddedTransaction()) {
// locking is a no-op in embedded transactions
return TRI_ERROR_NO_ERROR;
}
return TRI_LockCollectionTransaction(this->_trx, (TRI_transaction_cid_t) primary->base._info._cid, type);
return TRI_LockCollectionTransaction(_trx, (TRI_transaction_cid_t) primary->base._info._cid, type);
}
////////////////////////////////////////////////////////////////////////////////
@ -387,20 +376,20 @@ namespace triagens {
int unlockExplicit (TRI_primary_collection_t* const primary,
const TRI_transaction_type_e type) {
if (this->_trx == 0) {
if (_trx == 0) {
return TRI_ERROR_INTERNAL;
}
if (this->status() != TRI_TRANSACTION_RUNNING) {
if (getStatus() != TRI_TRANSACTION_RUNNING) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
if (this->isEmbedded()) {
if (this->isEmbeddedTransaction()) {
// locking is a no-op in embedded transactions
return TRI_ERROR_NO_ERROR;
}
return TRI_UnlockCollectionTransaction(this->_trx, (TRI_transaction_cid_t) primary->base._info._cid, type);
return TRI_UnlockCollectionTransaction(_trx, (TRI_transaction_cid_t) primary->base._info._cid, type);
}
////////////////////////////////////////////////////////////////////////////////
@ -622,6 +611,7 @@ namespace triagens {
}
TRI_shaped_json_t* shaped = TRI_ShapedJsonJson(primary->_shaper, json);
if (shaped == 0) {
return TRI_ERROR_ARANGO_SHAPER_FAILED;
}
@ -778,6 +768,15 @@ namespace triagens {
return res;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the pointer to the C transaction struct
/// DEPRECATED
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* getTrx () {
return this->_trx;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
@ -793,8 +792,178 @@ namespace triagens {
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy an embedded transaction
////////////////////////////////////////////////////////////////////////////////
void destroyEmbedded () {
TRI_ASSERT_DEBUG(_nestingLevel > 0);
TRI_ASSERT_DEBUG(_trx->_nestingLevel > 0);
_trx->_nestingLevel--;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy a top-level transaction
////////////////////////////////////////////////////////////////////////////////
void destroyToplevel () {
TRI_ASSERT_DEBUG(_nestingLevel == 0);
TRI_ASSERT_DEBUG(_trx->_nestingLevel == 0);
if (getStatus() == TRI_TRANSACTION_RUNNING) {
// auto abort a running transaction
this->abort();
}
// free the data associated with the transaction
freeTransaction();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief begin an embedded transaction
////////////////////////////////////////////////////////////////////////////////
inline int beginEmbedded () {
if (getStatus() != TRI_TRANSACTION_RUNNING) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief begin a top-level transaction
////////////////////////////////////////////////////////////////////////////////
inline int beginToplevel () {
if (getStatus() != TRI_TRANSACTION_CREATED) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
return TRI_StartTransaction(_trx, _hints);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief commit an embedded transaction
////////////////////////////////////////////////////////////////////////////////
inline int commitEmbedded () {
// return instantly if the transaction is embedded
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief commit a top-level transaction
////////////////////////////////////////////////////////////////////////////////
inline int commitToplevel () {
if (_trx->_type == TRI_TRANSACTION_READ) {
// a read transaction just finishes
return TRI_FinishTransaction(_trx);
}
// a write transaction commits
return TRI_CommitTransaction(_trx);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief abort an embedded transaction
////////////////////////////////////////////////////////////////////////////////
inline int abortEmbedded () {
if (_readOnly) {
// return instantly if the transaction is embedded
return TRI_ERROR_NO_ERROR;
}
// real abort
return TRI_AbortTransaction(_trx);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief abort a top-level transaction
////////////////////////////////////////////////////////////////////////////////
inline int abortToplevel () {
// real abort
return TRI_AbortTransaction(_trx);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief initialise the transaction
/// this will first check if the transaction is embedded in a parent
/// transaction. if not, it will create a transaction of its own
////////////////////////////////////////////////////////////////////////////////
int setupTransaction () {
// check in the context if we are running embedded
_trx = this->getParentTransaction();
if (_trx != 0) {
// yes, we are embedded
return setupEmbedded();
}
else {
return setupToplevel();
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set up an embedded transaction
////////////////////////////////////////////////////////////////////////////////
int setupEmbedded () {
_nestingLevel = ++_trx->_nestingLevel;
if (! this->isEmbeddable()) {
// we are embedded but this is disallowed...
LOGGER_WARNING("logic error. invalid nesting of transactions");
return TRI_ERROR_TRANSACTION_NESTED;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set up a top-level transaction
////////////////////////////////////////////////////////////////////////////////
int setupToplevel () {
_nestingLevel = 0;
// we are not embedded. now start our own transaction
_trx = TRI_CreateTransaction(_vocbase->_transactionContext);
if (_trx == 0) {
return TRI_ERROR_OUT_OF_MEMORY;
}
// register the transaction in the context
return this->registerTransaction(_trx);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free transaction
////////////////////////////////////////////////////////////////////////////////
int freeTransaction () {
TRI_ASSERT_DEBUG(! isEmbeddedTransaction());
if (_trx != 0) {
this->unregisterTransaction();
TRI_FreeTransaction(_trx);
_trx = 0;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief extract the "_key" attribute from a JSON object
/// TODO: move out of this class
////////////////////////////////////////////////////////////////////////////////
int getKey (TRI_json_t const* json, TRI_voc_key_t* key) {
@ -807,6 +976,7 @@ namespace triagens {
// check _key is there
const TRI_json_t* k = TRI_LookupArrayJson((TRI_json_t*) json, "_key");
if (k == 0) {
return TRI_ERROR_NO_ERROR;
}
@ -818,55 +988,54 @@ namespace triagens {
// _key is there and a string
*key = k->_value._string.data;
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create transaction
////////////////////////////////////////////////////////////////////////////////
int createTransaction () {
if (this->isEmbedded()) {
if (! this->isEmbeddable()) {
return TRI_ERROR_TRANSACTION_NESTED;
}
this->_trx = this->getParent();
if (this->_trx == 0) {
return TRI_ERROR_TRANSACTION_INVALID_STATE;
}
return TRI_ERROR_NO_ERROR;
}
this->_trx = TRI_CreateTransaction(_vocbase->_transactionContext, TRI_TRANSACTION_READ_REPEATABLE);
if (this->_trx == 0) {
return TRI_ERROR_OUT_OF_MEMORY;
}
this->registerTransaction(this->_trx);
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free transaction
/// @}
////////////////////////////////////////////////////////////////////////////////
int freeTransaction () {
assert(! this->isEmbedded());
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
if (this->_trx != 0) {
this->unregisterTransaction();
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////
TRI_FreeTransaction(this->_trx);
this->_trx = 0;
}
private:
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief the C transaction struct
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* _trx;
////////////////////////////////////////////////////////////////////////////////
/// @brief error that occurred on transaction initialisation (before begin())
////////////////////////////////////////////////////////////////////////////////
int _setupError;
////////////////////////////////////////////////////////////////////////////////
/// @brief how deep the transaction is down in a nested transaction structure
////////////////////////////////////////////////////////////////////////////////
int _nestingLevel;
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the transaction is read-only
////////////////////////////////////////////////////////////////////////////////
bool _readOnly;
////////////////////////////////////////////////////////////////////////////////
/// @brief transaction hints
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_hint_t _hints;
////////////////////////////////////////////////////////////////////////////////
/// @}
@ -895,39 +1064,6 @@ namespace triagens {
const CollectionNameResolver _resolver;
////////////////////////////////////////////////////////////////////////////////
/// @brief error that occurred on transaction initialisation (before begin())
////////////////////////////////////////////////////////////////////////////////
int _setupError;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the transaction is read-only
////////////////////////////////////////////////////////////////////////////////
bool _readOnly;
////////////////////////////////////////////////////////////////////////////////
/// @brief transaction hints
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_hint_t _hints;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -28,6 +28,8 @@
#ifndef TRIAGENS_UTILS_V8TRANSACTION_CONTEXT_H
#define TRIAGENS_UTILS_V8TRANSACTION_CONTEXT_H 1
#include <v8.h>
#include "V8/v8-globals.h"
namespace triagens {
@ -54,14 +56,7 @@ namespace triagens {
/// @brief create the context
////////////////////////////////////////////////////////////////////////////////
V8TransactionContext () : _previous(0) {
TRI_v8_global_t* v8g;
v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
if (v8g->_currentTransaction != 0) {
_previous = (TRI_transaction_t*) v8g->_currentTransaction;
}
V8TransactionContext () {
}
////////////////////////////////////////////////////////////////////////////////
@ -87,28 +82,26 @@ namespace triagens {
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the transaction is embedded
/// @brief get parent transaction (if any)
////////////////////////////////////////////////////////////////////////////////
inline bool isEmbedded () const {
return _previous != 0;
}
inline TRI_transaction_t* getParentTransaction () const {
TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
////////////////////////////////////////////////////////////////////////////////
/// @brief return the parent transaction if any
////////////////////////////////////////////////////////////////////////////////
if (v8g->_currentTransaction != 0) {
return (TRI_transaction_t*) v8g->_currentTransaction;
}
inline TRI_transaction_t* getParent () const {
return _previous;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief register the transaction in the context
////////////////////////////////////////////////////////////////////////////////
int registerTransaction (TRI_transaction_t* const trx) {
TRI_v8_global_t* v8g;
v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
inline int registerTransaction (TRI_transaction_t* const trx) const {
TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
v8g->_currentTransaction = trx;
return TRI_ERROR_NO_ERROR;
@ -118,36 +111,14 @@ namespace triagens {
/// @brief unregister the transaction from the context
////////////////////////////////////////////////////////////////////////////////
int unregisterTransaction () {
TRI_v8_global_t* v8g;
inline int unregisterTransaction () const {
TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData();
v8g->_currentTransaction = 0;
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief previous transaction
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* _previous;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1000,6 +1000,7 @@ static v8::Handle<v8::Value> SaveVocbaseCol (SingleCollectionWriteTransaction<Em
TRI_primary_collection_t* primary = trx->primaryCollection();
TRI_shaped_json_t* shaped = TRI_ShapedJsonV8Object(argv[0], primary->_shaper);
if (! holder.registerShapedJson(primary->_shaper, shaped)) {
return scope.Close(v8::ThrowException(
TRI_CreateErrorObject(TRI_errno(),
@ -1999,7 +2000,7 @@ static v8::Handle<v8::Value> JS_CreateCursor (v8::Arguments const& argv) {
TRI_CreateErrorObject(TRI_ERROR_ILLEGAL_OPTION,
"<list> must be a list")));
}
// extract objects
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(argv[0]);
TRI_json_t* json = TRI_ObjectToJson(array);
@ -4766,15 +4767,12 @@ static v8::Handle<v8::Value> JS_PropertiesVocbaseCol (v8::Arguments const& argv)
result->Set(v8g->JournalSizeKey, v8::Number::New(base->_info._maximalSize));
if (keyOptions != 0) {
result->Set(v8g->KeyOptionsKey, TRI_ObjectJson(keyOptions)->ToObject());
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, keyOptions);
}
else {
result->Set(v8g->KeyOptionsKey, v8::Array::New());
}
result->Set(v8g->WaitForSyncKey, base->_info._waitForSync ? v8::True() : v8::False());
if (keyOptions != 0) {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, keyOptions);
}
}
ReleaseCollection(collection);
@ -6006,10 +6004,11 @@ static v8::Handle<v8::Value> MapGetShapedJson (v8::Local<v8::String> name,
TRI_primary_collection_t* collection = barrier->_container->_collection;
// convert the JavaScript string to a string
string key = TRI_ObjectToString(name);
const string key = TRI_ObjectToString(name);
if (key == "") {
return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_ARANGO_ILLEGAL_NAME, "name must not be empty")));
if (key.size() == 0) {
// we must not throw a v8 exception here because this will cause follow up errors
return scope.Close(v8::Handle<v8::Value>());
}
if (TRI_IsSystemCollectionName(key.c_str())) {
@ -6020,9 +6019,6 @@ static v8::Handle<v8::Value> MapGetShapedJson (v8::Local<v8::String> name,
TRI_shaper_t* shaper = collection->_shaper;
TRI_shape_pid_t pid = shaper->findAttributePathByName(shaper, key.c_str());
// TRI_shape_sid_t sid;
// TRI_EXTRACT_SHAPE_IDENTIFIER_MARKER(sid, marker);
TRI_shaped_json_t document;
TRI_EXTRACT_SHAPED_JSON_MARKER(document, marker);
@ -6040,7 +6036,8 @@ static v8::Handle<v8::Value> MapGetShapedJson (v8::Local<v8::String> name,
}
}
else {
return scope.Close(v8::ThrowException(v8::String::New("cannot extract attribute")));
// we must not throw a v8 exception here because this will cause follow up errors
return scope.Close(v8::Handle<v8::Value>());
}
}

View File

@ -197,7 +197,7 @@ static void InitDatafile (TRI_datafile_t* datafile,
void* mmHandle,
TRI_voc_size_t maximalSize,
TRI_voc_size_t currentSize,
TRI_voc_fid_t tick,
TRI_voc_fid_t fid,
char* data) {
// filename is a string for physical datafiles, and NULL for anonymous regions
@ -210,7 +210,7 @@ static void InitDatafile (TRI_datafile_t* datafile,
}
datafile->_state = TRI_DF_STATE_READ;
datafile->_fid = tick;
datafile->_fid = fid;
datafile->_filename = filename;
datafile->_fd = fd;
@ -756,6 +756,7 @@ static TRI_datafile_t* OpenDatafile (char const* filename, bool ignoreErrors) {
////////////////////////////////////////////////////////////////////////////////
TRI_datafile_t* TRI_CreateDatafile (char const* filename,
TRI_voc_fid_t fid,
TRI_voc_size_t maximalSize) {
TRI_datafile_t* datafile;
TRI_df_marker_t* position;
@ -776,14 +777,14 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename,
// create either an anonymous or a physical datafile
if (filename == NULL) {
#ifdef TRI_HAVE_ANONYMOUS_MMAP
datafile = TRI_CreateAnonymousDatafile(maximalSize);
datafile = TRI_CreateAnonymousDatafile(fid, maximalSize);
#else
// system does not support anonymous mmap
return NULL;
#endif
}
else {
datafile = TRI_CreatePhysicalDatafile(filename, maximalSize);
datafile = TRI_CreatePhysicalDatafile(filename, fid, maximalSize);
}
if (datafile == NULL) {
@ -800,7 +801,7 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename,
header._version = TRI_DF_VERSION;
header._maximalSize = maximalSize;
header._fid = TRI_NewTickVocBase();
header._fid = fid;
// reserve space and write header to file
result = TRI_ReserveElementDatafile(datafile, header.base._size, &position);
@ -836,7 +837,8 @@ TRI_datafile_t* TRI_CreateDatafile (char const* filename,
#ifdef TRI_HAVE_ANONYMOUS_MMAP
TRI_datafile_t* TRI_CreateAnonymousDatafile (const TRI_voc_size_t maximalSize) {
TRI_datafile_t* TRI_CreateAnonymousDatafile (TRI_voc_fid_t fid,
const TRI_voc_size_t maximalSize) {
TRI_datafile_t* datafile;
ssize_t res;
void* data;
@ -894,7 +896,7 @@ TRI_datafile_t* TRI_CreateAnonymousDatafile (const TRI_voc_size_t maximalSize) {
mmHandle,
maximalSize,
0,
TRI_NewTickVocBase(),
fid,
data);
return datafile;
@ -906,7 +908,8 @@ TRI_datafile_t* TRI_CreateAnonymousDatafile (const TRI_voc_size_t maximalSize) {
/// @brief creates a new physical datafile
////////////////////////////////////////////////////////////////////////////////
TRI_datafile_t* TRI_CreatePhysicalDatafile (char const* filename,
TRI_datafile_t* TRI_CreatePhysicalDatafile (char const* filename,
TRI_voc_fid_t fid,
const TRI_voc_size_t maximalSize) {
TRI_datafile_t* datafile;
int fd;
@ -955,7 +958,7 @@ TRI_datafile_t* TRI_CreatePhysicalDatafile (char const* filename,
mmHandle,
maximalSize,
0,
TRI_NewTickVocBase(),
fid,
data);
return datafile;
@ -1246,6 +1249,8 @@ bool TRI_IterateDatafile (TRI_datafile_t* datafile,
// this function must not be called for non-physical datafiles
assert(datafile->isPhysical(datafile));
LOG_TRACE("iterating over datafile '%s', fid: %llu", datafile->getName(datafile), (unsigned long long) datafile->_fid);
ptr = datafile->_data;
end = datafile->_data + datafile->_currentSize;

View File

@ -437,6 +437,7 @@ TRI_df_skip_marker_t;
////////////////////////////////////////////////////////////////////////////////
TRI_datafile_t* TRI_CreateDatafile (char const*,
TRI_voc_fid_t fid,
TRI_voc_size_t);
////////////////////////////////////////////////////////////////////////////////
@ -449,7 +450,8 @@ TRI_datafile_t* TRI_CreateDatafile (char const*,
////////////////////////////////////////////////////////////////////////////////
#ifdef TRI_HAVE_ANONYMOUS_MMAP
TRI_datafile_t* TRI_CreateAnonymousDatafile (TRI_voc_size_t);
TRI_datafile_t* TRI_CreateAnonymousDatafile (TRI_voc_fid_t,
TRI_voc_size_t);
#endif
////////////////////////////////////////////////////////////////////////////////
@ -465,6 +467,7 @@ TRI_datafile_t* TRI_CreateAnonymousDatafile (TRI_voc_size_t);
////////////////////////////////////////////////////////////////////////////////
TRI_datafile_t* TRI_CreatePhysicalDatafile (char const*,
TRI_voc_fid_t,
TRI_voc_size_t);
////////////////////////////////////////////////////////////////////////////////

View File

@ -2112,7 +2112,7 @@ TRI_document_collection_t* TRI_CreateDocumentCollection (TRI_vocbase_t* vocbase,
if (document == NULL) {
TRI_FreeKeyGenerator(keyGenerator);
LOG_ERROR("cannot create document collection");
LOG_WARNING("cannot create key generator for document collection");
return NULL;
}

View File

@ -193,6 +193,10 @@ static int TraditionalGenerate (TRI_key_generator_t* const generator,
// user key is too long
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
}
else if (userKeyLength == 0) {
// user key is empty
return TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD;
}
// validate user-supplied key
if (regexec(&data->_regex, userKey, 0, NULL, 0) != 0) {

View File

@ -120,18 +120,22 @@ static char* FileTypeName (const bool compactor) {
/// @brief creates a journal or a compactor journal
////////////////////////////////////////////////////////////////////////////////
static TRI_datafile_t* CreateJournal (TRI_primary_collection_t* primary, bool compactor) {
static TRI_datafile_t* CreateJournal (TRI_primary_collection_t* primary,
bool compactor) {
TRI_col_header_marker_t cm;
TRI_collection_t* collection;
TRI_datafile_t* journal;
TRI_df_marker_t* position;
TRI_voc_fid_t fid;
int res;
collection = &primary->base;
fid = TRI_NewTickVocBase();
if (collection->_info._isVolatile) {
// in-memory collection
journal = TRI_CreateDatafile(NULL, collection->_info._maximalSize);
journal = TRI_CreateDatafile(NULL, fid, collection->_info._maximalSize);
}
else {
char* jname;
@ -139,14 +143,14 @@ static TRI_datafile_t* CreateJournal (TRI_primary_collection_t* primary, bool co
char* filename;
// construct a suitable filename (which is temporary at the beginning)
number = TRI_StringUInt64(TRI_NewTickVocBase());
number = TRI_StringUInt64(fid);
jname = TRI_Concatenate3String("temp-", number, ".db");
filename = TRI_Concatenate2File(collection->_directory, jname);
TRI_FreeString(TRI_CORE_MEM_ZONE, number);
TRI_FreeString(TRI_CORE_MEM_ZONE, jname);
journal = TRI_CreateDatafile(filename, collection->_info._maximalSize);
journal = TRI_CreateDatafile(filename, fid, collection->_info._maximalSize);
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
}
@ -194,6 +198,8 @@ static TRI_datafile_t* CreateJournal (TRI_primary_collection_t* primary, bool co
return NULL;
}
TRI_ASSERT_DEBUG(fid == journal->_fid);
// if a physical file, we can rename it from the temporary name to the correct name
if (journal->isPhysical(journal)) {

View File

@ -51,10 +51,12 @@ static bool CreateJournal (TRI_shape_collection_t* collection) {
TRI_col_header_marker_t cm;
TRI_datafile_t* journal;
TRI_df_marker_t* position;
TRI_voc_fid_t fid;
char* filename;
int res;
bool isVolatile;
fid = TRI_NewTickVocBase();
isVolatile = collection->base._info._isVolatile;
if (isVolatile) {
@ -65,19 +67,19 @@ static bool CreateJournal (TRI_shape_collection_t* collection) {
char* jname;
char* number;
number = TRI_StringUInt64(TRI_NewTickVocBase());
if (! number) {
number = TRI_StringUInt64(fid);
if (number == NULL) {
return false;
}
jname = TRI_Concatenate3String("journal-", number, ".db");
jname = TRI_Concatenate3String("temp-", number, ".db");
TRI_FreeString(TRI_CORE_MEM_ZONE, number);
filename = TRI_Concatenate2File(collection->base._directory, jname);
TRI_FreeString(TRI_CORE_MEM_ZONE, jname);
}
journal = TRI_CreateDatafile(filename, collection->base._info._maximalSize);
journal = TRI_CreateDatafile(filename, fid, collection->base._info._maximalSize);
if (filename != NULL) {
TRI_FreeString(TRI_CORE_MEM_ZONE, filename);
@ -99,6 +101,7 @@ static bool CreateJournal (TRI_shape_collection_t* collection) {
LOG_TRACE("created a new shape journal '%s'", journal->getName(journal));
TRI_ASSERT_DEBUG(fid == journal->_fid);
if (journal->isPhysical(journal)) {
char* jname;
@ -106,7 +109,7 @@ static bool CreateJournal (TRI_shape_collection_t* collection) {
bool ok;
// and use the correct name
number = TRI_StringUInt64(journal->_fid);
number = TRI_StringUInt64(fid);
jname = TRI_Concatenate3String("journal-", number, ".db");
filename = TRI_Concatenate2File(collection->base._directory, jname);

View File

@ -565,7 +565,7 @@ static TRI_transaction_collection_t* CreateCollection (const TRI_transaction_cid
////////////////////////////////////////////////////////////////////////////////
static void FreeCollection (TRI_transaction_collection_t* collection) {
assert(collection);
TRI_ASSERT_DEBUG(collection != NULL);
#if 0
DestroyTransactionList(&collection->_writeTransactions);
@ -582,10 +582,10 @@ static int LockCollection (TRI_transaction_collection_t* collection,
const TRI_transaction_type_e type) {
TRI_primary_collection_t* primary;
assert(collection != NULL);
assert(collection->_collection != NULL);
assert(collection->_collection->_collection != NULL);
assert(collection->_locked == false);
TRI_ASSERT_DEBUG(collection != NULL);
TRI_ASSERT_DEBUG(collection->_collection != NULL);
TRI_ASSERT_DEBUG(collection->_collection->_collection != NULL);
TRI_ASSERT_DEBUG(collection->_locked == false);
primary = collection->_collection->_collection;
@ -609,10 +609,10 @@ static int UnlockCollection (TRI_transaction_collection_t* collection,
const TRI_transaction_type_e type) {
TRI_primary_collection_t* primary;
assert(collection != NULL);
assert(collection->_collection != NULL);
assert(collection->_collection->_collection != NULL);
assert(collection->_locked == true);
TRI_ASSERT_DEBUG(collection != NULL);
TRI_ASSERT_DEBUG(collection->_collection != NULL);
TRI_ASSERT_DEBUG(collection->_collection->_collection != NULL);
TRI_ASSERT_DEBUG(collection->_locked == true);
primary = collection->_collection->_collection;
@ -904,10 +904,10 @@ static int UpdateTransactionStatus (TRI_transaction_t* const trx,
#endif
int res;
assert(trx->_status == TRI_TRANSACTION_RUNNING);
assert(status == TRI_TRANSACTION_COMMITTED ||
status == TRI_TRANSACTION_ABORTED ||
status == TRI_TRANSACTION_FINISHED);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_RUNNING);
TRI_ASSERT_DEBUG(status == TRI_TRANSACTION_COMMITTED ||
status == TRI_TRANSACTION_ABORTED ||
status == TRI_TRANSACTION_FINISHED);
#if 0
context = trx->_context;
@ -964,7 +964,7 @@ static int RegisterTransaction (TRI_transaction_t* const trx) {
#endif
int res;
assert(trx->_status == TRI_TRANSACTION_CREATED);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_CREATED);
#if 0
context = trx->_context;
@ -1016,11 +1016,11 @@ static int RegisterTransaction (TRI_transaction_t* const trx) {
/// @brief create a new transaction container
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* TRI_CreateTransaction (TRI_transaction_context_t* const context,
const TRI_transaction_isolation_level_e isolationLevel) {
TRI_transaction_t* TRI_CreateTransaction (TRI_transaction_context_t* const context) {
TRI_transaction_t* trx;
trx = (TRI_transaction_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_transaction_t), false);
if (trx == NULL) {
// out of memory
return NULL;
@ -1031,8 +1031,8 @@ TRI_transaction_t* TRI_CreateTransaction (TRI_transaction_context_t* const conte
trx->_id._localId = 0;
trx->_status = TRI_TRANSACTION_CREATED;
trx->_type = TRI_TRANSACTION_READ;
trx->_isolationLevel = isolationLevel;
trx->_hints = 0;
trx->_nestingLevel = 0;
TRI_InitVectorPointer2(&trx->_collections, TRI_UNKNOWN_MEM_ZONE, 2);
@ -1046,7 +1046,7 @@ TRI_transaction_t* TRI_CreateTransaction (TRI_transaction_context_t* const conte
void TRI_FreeTransaction (TRI_transaction_t* const trx) {
size_t i;
assert(trx);
TRI_ASSERT_DEBUG(trx != NULL);
if (trx->_status == TRI_TRANSACTION_RUNNING) {
TRI_AbortTransaction(trx);
@ -1117,6 +1117,7 @@ int TRI_UnlockCollectionTransaction (TRI_transaction_t* const trx,
n = trx->_collections._length;
// shouldn't this be in reverse order?
for (i = 0; i < n; ++i) {
TRI_transaction_collection_t* collection = TRI_AtVectorPointer(&trx->_collections, i);
@ -1132,6 +1133,7 @@ int TRI_UnlockCollectionTransaction (TRI_transaction_t* const trx,
return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
}
/*
////////////////////////////////////////////////////////////////////////////////
/// @brief return whether the transaction consists only of a single operation
////////////////////////////////////////////////////////////////////////////////
@ -1139,7 +1141,9 @@ int TRI_UnlockCollectionTransaction (TRI_transaction_t* const trx,
bool TRI_IsSingleOperationTransaction (const TRI_transaction_t* const trx) {
return (trx->_hints & (TRI_transaction_hint_t) TRI_TRANSACTION_HINT_SINGLE_OPERATION) != 0;
}
*/
/*
////////////////////////////////////////////////////////////////////////////////
/// @brief return whether the transaction spans multiple write collections
////////////////////////////////////////////////////////////////////////////////
@ -1171,6 +1175,7 @@ bool TRI_IsMultiCollectionWriteTransaction (const TRI_transaction_t* const trx)
return false;
}
*/
////////////////////////////////////////////////////////////////////////////////
/// @brief return the local id of a transaction
@ -1225,8 +1230,8 @@ TRI_vocbase_col_t* TRI_CheckCollectionTransaction (TRI_transaction_t* const trx,
TRI_transaction_collection_t* collection;
size_t i, n;
assert(trx->_status == TRI_TRANSACTION_CREATED ||
trx->_status == TRI_TRANSACTION_RUNNING);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_CREATED ||
trx->_status == TRI_TRANSACTION_RUNNING);
// check if we already have got this collection in the _collections vector
// the vector is sorted by collection names
@ -1263,7 +1268,7 @@ int TRI_AddCollectionTransaction (TRI_transaction_t* const trx,
TRI_transaction_collection_t* collection;
size_t i, n;
assert(trx->_status == TRI_TRANSACTION_CREATED);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_CREATED);
// upgrade transaction type if required
if (type == TRI_TRANSACTION_WRITE && trx->_type == TRI_TRANSACTION_READ) {
@ -1362,7 +1367,7 @@ int TRI_AddDelayedReadCollectionTransaction (TRI_transaction_t* const trx,
int TRI_StartTransaction (TRI_transaction_t* const trx, TRI_transaction_hint_t hints) {
int res;
assert(trx->_status == TRI_TRANSACTION_CREATED);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_CREATED);
trx->_hints = hints;
@ -1396,7 +1401,7 @@ int TRI_StartTransaction (TRI_transaction_t* const trx, TRI_transaction_hint_t h
int TRI_CommitTransaction (TRI_transaction_t* const trx) {
int res;
assert(trx->_status == TRI_TRANSACTION_RUNNING);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_RUNNING);
res = UpdateTransactionStatus(trx, TRI_TRANSACTION_COMMITTED);
ReleaseCollections(trx);
@ -1411,7 +1416,7 @@ int TRI_CommitTransaction (TRI_transaction_t* const trx) {
int TRI_AbortTransaction (TRI_transaction_t* const trx) {
int res;
assert(trx->_status == TRI_TRANSACTION_RUNNING);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_RUNNING);
res = UpdateTransactionStatus(trx, TRI_TRANSACTION_ABORTED);
ReleaseCollections(trx);
@ -1424,10 +1429,10 @@ int TRI_AbortTransaction (TRI_transaction_t* const trx) {
////////////////////////////////////////////////////////////////////////////////
int TRI_FinishTransaction (TRI_transaction_t* const trx) {
int res;
TRI_transaction_status_e status;
int res;
assert(trx->_status == TRI_TRANSACTION_RUNNING);
TRI_ASSERT_DEBUG(trx->_status == TRI_TRANSACTION_RUNNING);
if (trx->_type == TRI_TRANSACTION_READ) {
// read transactions

View File

@ -84,17 +84,6 @@ typedef struct TRI_transaction_id_s {
}
TRI_transaction_id_t;
////////////////////////////////////////////////////////////////////////////////
/// @brief transaction isolation level
////////////////////////////////////////////////////////////////////////////////
typedef enum {
TRI_TRANSACTION_READ_UNCOMMITED = 1,
TRI_TRANSACTION_READ_COMMITTED = 2,
TRI_TRANSACTION_READ_REPEATABLE = 3
}
TRI_transaction_isolation_level_e;
////////////////////////////////////////////////////////////////////////////////
/// @brief transaction type
////////////////////////////////////////////////////////////////////////////////
@ -327,13 +316,13 @@ TRI_transaction_hint_e;
////////////////////////////////////////////////////////////////////////////////
typedef struct TRI_transaction_s {
TRI_transaction_context_t* _context; // global context object
TRI_transaction_id_t _id; // id of transaction
TRI_transaction_type_e _type; // access type (read|write)
TRI_transaction_status_e _status; // current status
TRI_transaction_isolation_level_e _isolationLevel; // isolation level
TRI_vector_pointer_t _collections; // list of participating collections
TRI_transaction_hint_t _hints; // hints;
TRI_transaction_context_t* _context; // global context object
TRI_transaction_id_t _id; // id of transaction
TRI_transaction_type_e _type; // access type (read|write)
TRI_transaction_status_e _status; // current status
TRI_vector_pointer_t _collections; // list of participating collections
TRI_transaction_hint_t _hints; // hints;
int _nestingLevel;
}
TRI_transaction_t;
@ -354,8 +343,7 @@ TRI_transaction_t;
/// @brief create a new transaction container
////////////////////////////////////////////////////////////////////////////////
TRI_transaction_t* TRI_CreateTransaction (TRI_transaction_context_t* const,
const TRI_transaction_isolation_level_e);
TRI_transaction_t* TRI_CreateTransaction (TRI_transaction_context_t* const);
////////////////////////////////////////////////////////////////////////////////
/// @brief free a transaction container
@ -376,17 +364,21 @@ void TRI_FreeTransaction (TRI_transaction_t* const);
/// @{
////////////////////////////////////////////////////////////////////////////////
/*
////////////////////////////////////////////////////////////////////////////////
/// @brief return whether the transaction consists only of a single operation
////////////////////////////////////////////////////////////////////////////////
bool TRI_IsSingleOperationTransaction (const TRI_transaction_t* const);
*/
/*
////////////////////////////////////////////////////////////////////////////////
/// @brief return whether the transaction spans multiple write collections
////////////////////////////////////////////////////////////////////////////////
bool TRI_IsMultiCollectionWriteTransaction (const TRI_transaction_t* const);
*/
////////////////////////////////////////////////////////////////////////////////
/// @brief return the local id of a transaction

View File

@ -483,7 +483,7 @@
////////////////////////////////////////////////////////////////////////////////
var quoteSingleJsonCharacter = function (c) {
if (characterQuoteCache.hasOwnProperty[c]) {
if (characterQuoteCache.hasOwnProperty(c)) {
return characterQuoteCache[c];
}

View File

@ -169,7 +169,7 @@ ArangoQueryCursor.prototype.hasNext = function () {
////////////////////////////////////////////////////////////////////////////////
ArangoQueryCursor.prototype.next = function () {
if (!this._hasNext) {
if (! this._hasNext) {
throw "No more results";
}
@ -237,10 +237,6 @@ ArangoQueryCursor.prototype.dispose = function () {
////////////////////////////////////////////////////////////////////////////////
ArangoQueryCursor.prototype.count = function () {
if (! this.data.id) {
throw "cursor has been disposed";
}
return this.data.count;
};

View File

@ -129,7 +129,8 @@ ArangoStatement.prototype.parse = function () {
ArangoStatement.prototype.explain = function () {
var body = {
query: this._query
query: this._query,
bindVars: this._bindVars
};
var requestResult = this._database._connection.POST(

View File

@ -249,7 +249,7 @@ var documentView = Backbone.View.extend({
},
escaped: function (value) {
return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#38;");
return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
},
updateLocalDocumentStorage: function () {

View File

@ -227,7 +227,7 @@ var documentsView = Backbone.View.extend({
//value.attributes.id,
value.attributes.key,
//value.attributes.rev,
'<pre class=prettify title="'+self.escaped(JSON.stringify(value.attributes.content)) +'">' + self.cutByResolution(JSON.stringify(value.attributes.content)) + '</pre>',
'<pre class="prettify" title="'+self.escaped(JSON.stringify(value.attributes.content)) +'">' + self.cutByResolution(JSON.stringify(value.attributes.content)) + '</pre>',
'<button class="enabled" id="deleteDoc"><img src="/_admin/html/img/icon_delete.png" width="16" height="16"></button>'
]);
});
@ -303,7 +303,7 @@ var documentsView = Backbone.View.extend({
return this.escaped(string);
},
escaped: function (value) {
return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#38;");
return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}
});

View File

@ -168,7 +168,7 @@ ArangoQueryCursor.prototype.hasNext = function () {
////////////////////////////////////////////////////////////////////////////////
ArangoQueryCursor.prototype.next = function () {
if (!this._hasNext) {
if (! this._hasNext) {
throw "No more results";
}
@ -236,10 +236,6 @@ ArangoQueryCursor.prototype.dispose = function () {
////////////////////////////////////////////////////////////////////////////////
ArangoQueryCursor.prototype.count = function () {
if (! this.data.id) {
throw "cursor has been disposed";
}
return this.data.count;
};

View File

@ -128,7 +128,8 @@ ArangoStatement.prototype.parse = function () {
ArangoStatement.prototype.explain = function () {
var body = {
query: this._query
query: this._query,
bindVars: this._bindVars
};
var requestResult = this._database._connection.POST(

View File

@ -483,7 +483,7 @@
////////////////////////////////////////////////////////////////////////////////
var quoteSingleJsonCharacter = function (c) {
if (characterQuoteCache.hasOwnProperty[c]) {
if (characterQuoteCache.hasOwnProperty(c)) {
return characterQuoteCache[c];
}

View File

@ -1,159 +0,0 @@
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, plusplus: true */
/*global _, require, db, exports */
var Frank,
BaseMiddleware,
_ = require("underscore"),
arangodb = require("org/arangodb"),
internal = {};
var db = arangodb.db;
internal.createUrlObject = function (url, constraint, method) {
var urlObject = {};
if (!_.isString(url)) {
throw "URL has to be a String";
}
urlObject.match = url;
urlObject.methods = [method];
if (!_.isNull(constraint)) {
urlObject.constraint = constraint;
}
return urlObject;
};
Frank = function (options) {
var urlPrefix, templateCollection;
options = options || {};
urlPrefix = options.urlPrefix;
templateCollection = options.templateCollection;
this.routingInfo = {
routes: []
};
if (_.isString(urlPrefix)) {
this.routingInfo.urlPrefix = urlPrefix;
}
if (_.isString(templateCollection)) {
this.routingInfo.templateCollection = db._collection(templateCollection) ||
db._create(templateCollection);
}
};
_.extend(Frank.prototype, {
handleRequest: function (method, route, argument1, argument2) {
var newRoute = {}, options, handler;
if (_.isUndefined(argument2)) {
handler = argument1;
options = {};
} else {
options = argument1;
handler = argument2;
}
newRoute.url = internal.createUrlObject(route, options.constraint, method);
newRoute.handler = handler;
this.routingInfo.routes.push(newRoute);
},
head: function (route, argument1, argument2) {
this.handleRequest("head", route, argument1, argument2);
},
get: function (route, argument1, argument2) {
this.handleRequest("get", route, argument1, argument2);
},
post: function (route, argument1, argument2) {
this.handleRequest("post", route, argument1, argument2);
},
put: function (route, argument1, argument2) {
this.handleRequest("put", route, argument1, argument2);
},
patch: function (route, argument1, argument2) {
this.handleRequest("patch", route, argument1, argument2);
},
'delete': function (route, argument1, argument2) {
this.handleRequest("delete", route, argument1, argument2);
}
});
BaseMiddleware = function (templateCollection) {
var middleware = function (request, response, options, next) {
var responseFunctions;
responseFunctions = {
status: function (code) {
this.responseCode = code;
},
set: function (key, value) {
var attributes = {};
if (_.isUndefined(value)) {
attributes = key;
} else {
attributes[key] = value;
}
_.each(attributes, function (value, key) {
key = key.toLowerCase();
this.headers = this.headers || {};
this.headers[key] = value;
if (key === "content-type") {
this.contentType = value;
}
}, this);
},
json: function (obj) {
this.contentType = "application/json";
this.body = JSON.stringify(obj);
},
render: function (templatePath, data) {
var template;
if (_.isUndefined(templateCollection)) {
throw "No template collection has been provided when creating a new Frank";
}
template = templateCollection.firstExample({path: templatePath });
if (_.isNull(template)) {
throw "Template '" + templatePath + "' does not exist";
}
if (template.templateLanguage !== "underscore") {
throw "Unknown template language '" + template.templateLanguage + "'";
}
this.body = _.template(template.content, data);
this.contentType = template.contentType;
}
};
response = _.extend(response, responseFunctions);
next();
};
return middleware;
};
exports.Frank = Frank;
exports.BaseMiddleware = BaseMiddleware;

View File

@ -0,0 +1,534 @@
/*jslint indent: 2, nomen: true, maxlen: 100 */
/*global require, exports */
// Fuxx is an easy way to create APIs and simple web applications
// from within **ArangoDB**.
// It is inspired by Sinatra, the classy Ruby web framework. If FuxxApplication is Sinatra,
// [ArangoDB Actions](http://www.arangodb.org/manuals/current/UserManualActions.html)
// are the corresponding `Rack`. They provide all the HTTP goodness.
//
// So let's get started, shall we?
// FuxxApplication uses Underscore internally. This library is wonderful.
var FuxxApplication,
BaseMiddleware,
FormatMiddleware,
_ = require("underscore"),
db = require("org/arangodb").db,
internal = {};
// ArangoDB uses a certain structure we refer to as `UrlObject`.
// With the following function (which is only internal, and not
// exported) you can create an UrlObject with a given URL,
// a constraint and a method. For example:
//
// internal.createUrlObject('/lecker/gans', null, 'get')
internal.createUrlObject = function (url, constraint, method) {
'use strict';
var urlObject = {};
if (!_.isString(url)) {
throw "URL has to be a String";
}
urlObject.match = url;
urlObject.methods = [method];
if (!_.isNull(constraint)) {
urlObject.constraint = constraint;
}
return urlObject;
};
// ## Creating a new Application
// And that's FuxxApplication. It's a constructor, so call it like this:
//
// app = new FuxxApplication({
// urlPrefix: "/wiese",
// templateCollection: "my_templates"
// })
//
// It takes two optional arguments as displayed above:
//
// * **The URL Prefix:** All routes you define within will be prefixed with it
// * **The Template Collection:** More information in the template section
FuxxApplication = function (options) {
'use strict';
var urlPrefix, templateCollection, myMiddleware;
options = options || {};
urlPrefix = options.urlPrefix;
templateCollection = options.templateCollection;
this.routingInfo = {
routes: []
};
this.helperCollection = {};
if (_.isString(urlPrefix)) {
this.routingInfo.urlPrefix = urlPrefix;
}
if (_.isString(templateCollection)) {
this.routingInfo.templateCollection = db._collection(templateCollection) ||
db._create(templateCollection);
myMiddleware = new BaseMiddleware(templateCollection, this.helperCollection);
} else {
myMiddleware = new BaseMiddleware();
}
this.routingInfo.middleware = [
{
url: {match: "/*"},
action: {callback: myMiddleware}
}
];
};
// ## The functions on a created FuxxApplication
//
// When you have created your FuxxApplication you can now define routes
// on it. You provide each with a function that will handle
// the request. It gets two arguments (four, to be honest. But the
// other two are not relevant for now):
//
// * The request object
// * The response object
//
// You can find more about those in their individual sections.
_.extend(FuxxApplication.prototype, {
// The `handleRequest` method is the raw way to create a new
// route. You probably wont call it directly, but it is used
// in the other request methods:
//
// app.handleRequest("get", "/gaense", function (req, res) {
// //handle the request
// });
handleRequest: function (method, route, argument1, argument2) {
'use strict';
var newRoute = {}, options, callback;
if (_.isUndefined(argument2)) {
callback = argument1;
options = {};
} else {
options = argument1;
callback = argument2;
}
newRoute.url = internal.createUrlObject(route, options.constraint, method);
newRoute.action = {
callback: String(callback)
};
this.routingInfo.routes.push(newRoute);
},
// ### Handle a `head` request
// This handles requests from the HTTP verb `head`.
// As with all other requests you can give an option as the third
// argument, or leave it blank. You have to give a function as
// the last argument however. It will get a request and response
// object as its arguments
head: function (route, argument1, argument2) {
'use strict';
this.handleRequest("head", route, argument1, argument2);
},
// ### Manage a `get` request
// This handles requests from the HTTP verb `get`.
// See above for the arguments you can give. An example:
//
// app.get('/gaense/stall', function (req, res) {
// // Take this request and deal with it!
// });
get: function (route, argument1, argument2) {
'use strict';
this.handleRequest("get", route, argument1, argument2);
},
// ### Tackle a `post` request
// This handles requests from the HTTP verb `post`.
// See above for the arguments you can give. An example:
//
// app.post('/gaense/stall', function (req, res) {
// // Take this request and deal with it!
// });
post: function (route, argument1, argument2) {
'use strict';
this.handleRequest("post", route, argument1, argument2);
},
// ### Sort out a `put` request
// This handles requests from the HTTP verb `put`.
// See above for the arguments you can give. An example:
//
// app.put('/gaense/stall', function (req, res) {
// // Take this request and deal with it!
// });
put: function (route, argument1, argument2) {
'use strict';
this.handleRequest("put", route, argument1, argument2);
},
// ### Take charge of a `patch` request
// This handles requests from the HTTP verb `patch`.
// See above for the arguments you can give. An example:
//
// app.patch('/gaense/stall', function (req, res) {
// // Take this request and deal with it!
// });
patch: function (route, argument1, argument2) {
'use strict';
this.handleRequest("patch", route, argument1, argument2);
},
// ### Respond to a `delete` request
// This handles requests from the HTTP verb `delete`.
// See above for the arguments you can give.
// **A word of warning:** Do not forget that `delete` is
// a reserved word in JavaScript so call it as follows:
//
// app['delete']('/gaense/stall', function (req, res) {
// // Take this request and deal with it!
// });
'delete': function (route, argument1, argument2) {
'use strict';
this.handleRequest("delete", route, argument1, argument2);
},
// ## Before and After Hooks
// You can use the following two functions to do something
// before or respectively after the normal routing process
// is happening. You could use that for logging or to manipulate
// the request or response (translate it to a certain format for
// example).
// ### Before
// The before function takes a path on which it should watch
// and a function that it should execute before the routing
// takes place. If you do omit the path, the function will
// be executed before each request, no matter the path.
// Your function gets a Request and a Response object.
// An example:
//
// app.before('/high/way', function(req, res) {
// //Do some crazy request logging
// });
before: function (path, func) {
'use strict';
if (_.isUndefined(func)) {
func = path;
path = "/*";
}
this.routingInfo.middleware.push({
url: {match: path},
action: {callback: function (req, res, opts, next) {
func(req, res);
next();
}}
});
},
// ### After
// This works pretty similar to the before function.
// But it acts after the execution of the handlers
// (Big surprise, I suppose).
//
// An example:
//
// app.after('/high/way', function(req, res) {
// //Do some crazy response logging
// });
after: function (path, func) {
'use strict';
if (_.isUndefined(func)) {
func = path;
path = "/*";
}
this.routingInfo.middleware.push({
url: {match: path},
action: {callback: function (req, res, opts, next) {
next();
func(req, res);
}}
});
},
// ## The ViewHelper concept
// If you want to use a function inside your templates, the ViewHelpers
// will come to rescue you. Define them on your app like this:
//
// app.helper("link_to", function (identifier) {
// return urlRepository[identifier];
// });
//
// Then you can just call this function in your template's JavaScript
// blocks.
helper: function (name, func) {
'use strict';
this.helperCollection[name] = func;
},
// ## Shortform for using the FormatMiddleware
//
// More information about the FormatMiddleware in the corresponding section.
// This is a shortcut to add the middleware to your application:
//
// app.accepts(["json"], "json");
accepts: function (allowedFormats, defaultFormat) {
'use strict';
this.routingInfo.middleware.push({
url: { match: "/*" },
action: { callback: new FormatMiddleware(allowedFormats, defaultFormat) }
});
}
});
// ## The Base Middleware
// The `BaseMiddleware` manipulates the request and response
// objects to give you a nicer API.
BaseMiddleware = function (templateCollection, helperCollection) {
'use strict';
var middleware = function (request, response, options, next) {
var responseFunctions, requestFunctions;
// ### The Request Object
// Every request object has the following attributes from the underlying Actions:
//
// * path: The complete path as supplied by the user
// * prefix
// * suffix
// * urlParameters
//
// FuxxApplication currently leaves those unmodified.
responseFunctions = {};
// ### The Response Object
// Every response object has the following attributes from the underlying Actions:
//
// * contentType
// * responseCode
// * body
// * headers (an object)
//
// FuxxApplication adds the following methods to this response object.
responseFunctions = {
// ### The straightforward `status` function
// Set the status code of your response, for example:
//
// response.status(404);
status: function (code) {
this.responseCode = code;
},
// ### The radical `set` function
// Set a header attribute, for example:
//
// response.set("Content-Length", 123);
// response.set("Content-Type", "text/plain");
//
// or alternatively:
//
// response.set({
// "Content-Length": "123",
// "Content-Type": "text/plain"
// });
set: function (key, value) {
var attributes = {};
if (_.isUndefined(value)) {
attributes = key;
} else {
attributes[key] = value;
}
_.each(attributes, function (value, key) {
key = key.toLowerCase();
this.headers = this.headers || {};
this.headers[key] = value;
if (key === "content-type") {
this.contentType = value;
}
}, this);
},
// ### The magical `json` function
// Set the content type to JSON and the body to the
// JSON encoded object you provided.
//
// response.json({'born': 'December 12, 1915'});
json: function (obj) {
this.contentType = "application/json";
this.body = JSON.stringify(obj);
},
// ### The mysterious `render` function
// If you initialize your FuxxApplication with a `templateCollection`,
// you're in luck now.
// It expects documents in the following form in this collection:
//
// {
// path: "high/way",
// content: "hallo <%= username %>",
// contentType: "text/plain",
// templateLanguage: "underscore"
// }
//
// The `content` is the string that will be rendered by the template
// processor. The `contentType` is the type of content that results
// from this call. And with the `templateLanguage` you can choose
// your template processor. There is only one choice now: `underscore`.
//
// If you call render, FuxxApplication will look
// into the this collection and search by the path attribute.
// It will then render the template with the given data:
//
// response.render("high/way", {username: 'FuxxApplication'})
//
// Which would set the body of the response to `hello FuxxApplication` with the
// template defined above. It will also set the `contentType` to
// `text/plain` in this case.
//
// In addition to the attributes you provided, you also have access to
// all your view helpers. How to define them? Read above in the
// ViewHelper section.
render: function (templatePath, data) {
var template;
if (_.isUndefined(templateCollection)) {
throw "No template collection has been provided when creating a new FuxxApplication";
}
template = templateCollection.firstExample({path: templatePath });
if (_.isNull(template)) {
throw "Template '" + templatePath + "' does not exist";
}
if (template.templateLanguage !== "underscore") {
throw "Unknown template language '" + template.templateLanguage + "'";
}
this.body = _.template(template.content, _.extend(data, helperCollection));
this.contentType = template.contentType;
}
};
// Now enhance the request and response as described above and call next at the
// end of this middleware (otherwise your application would never
// be executed. Would be a shame, really).
request = _.extend(request, requestFunctions);
response = _.extend(response, responseFunctions);
next();
};
return middleware;
};
// ## The Format Middleware
// Unlike the `BaseMiddleware` this Middleware is only loaded if you
// want it. This Middleware gives you Rails-like format handling via
// the `extension` of the URL or the accept header.
// Say you request an URL like `/people.json`:
// The `FormatMiddleware` will set the format of the request to JSON
// and then delete the `.json` from the request. You can therefore write
// handlers that do not take an `extension` into consideration and instead
// handle the format via a simple String.
// To determine the format of the request it checks the URL and then
// the `accept` header. If one of them gives a format or both give
// the same, the format is set. If the formats are not the same,
// an error is raised.
//
// Use it by calling:
//
// FormatMiddleware = require('fuxx').FormatMiddleware;
// app.before("/*", FormatMiddleware.new(['json']));
//
// or the shortcut:
//
// app.accepts(['json']);
//
// In both forms you can give a default format as a second parameter,
// if no format could be determined. If you give no `defaultFormat` this
// case will be handled as an error.
FormatMiddleware = function (allowedFormats, defaultFormat) {
'use strict';
var middleware, urlFormatToMime, mimeToUrlFormat, determinePathAndFormat;
urlFormatToMime = {
json: "application/json",
html: "text/html",
txt: "text/plain"
};
mimeToUrlFormat = {
"application/json": "json",
"text/html": "html",
"text/plain": "txt"
};
determinePathAndFormat = function (path, headers) {
var parsed = {
contentType: headers.accept
};
path = path.split('.');
if (path.length === 1) {
parsed.path = path.join();
} else {
parsed.format = path.pop();
parsed.path = path.join('.');
}
if (parsed.contentType === undefined && parsed.format === undefined) {
parsed.format = defaultFormat;
parsed.contentType = urlFormatToMime[defaultFormat];
} else if (parsed.contentType === undefined) {
parsed.contentType = urlFormatToMime[parsed.format];
} else if (parsed.format === undefined) {
parsed.format = mimeToUrlFormat[parsed.contentType];
}
if (parsed.format !== mimeToUrlFormat[parsed.contentType]) {
parsed.error = "Contradiction between Accept Header and URL.";
}
if (allowedFormats.indexOf(parsed.format) < 0) {
parsed.error = "Format '" + parsed.format + "' is not allowed.";
}
return parsed;
};
middleware = function (request, response, options, next) {
var parsed = determinePathAndFormat(request.path, request.headers);
if (parsed.error === undefined) {
request.path = parsed.path;
request.format = parsed.format;
response.contentType = parsed.contentType;
next();
} else {
response.responseCode = 406;
response.body = parsed.error;
}
};
return middleware;
};
// We finish off with exporting FuxxApplication and the middlewares.
// Everything else will remain our secret.
//
// Fin.
exports.FuxxApplication = FuxxApplication;
exports.BaseMiddleware = BaseMiddleware;
exports.FormatMiddleware = FormatMiddleware;

View File

@ -0,0 +1,188 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief test attribute naming
///
/// @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 Dr. Frank Celler
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var arangodb = require("org/arangodb");
var db = arangodb.db;
var wait = require("internal").wait;
// -----------------------------------------------------------------------------
// --SECTION-- attributes
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test attributes
////////////////////////////////////////////////////////////////////////////////
function AttributesSuite () {
var cn = "UnitTestsCollectionAttributes";
var c = null;
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
db._drop(cn);
c = db._create(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
c.unload();
c.drop();
c = null;
wait(0.0);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief no attributes
////////////////////////////////////////////////////////////////////////////////
testNoAttributes : function () {
var doc = { };
var d1 = c.save(doc);
var d2 = c.document(d1._id);
delete d1.error;
assertEqual(d1, d2);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief empty attribute name
////////////////////////////////////////////////////////////////////////////////
testEmptyAttribute : function () {
var doc = { "" : "foo" };
var d1 = c.save(doc);
var d2 = c.document(d1._id);
delete d1.error;
var i;
for (i in d2) {
if (d2.hasOwnProperty(i)) {
assertTrue(i !== "");
}
}
assertEqual(d1, d2);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief query empty attribute name
////////////////////////////////////////////////////////////////////////////////
testQueryEmptyAttribute : function () {
var doc = { "" : "foo" };
c.save(doc);
var docs = c.toArray();
assertEqual(1, docs.length);
var d = docs[0];
var i;
for (i in d) {
if (d.hasOwnProperty(i)) {
assertTrue(i !== "");
}
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief reserved attribute names
////////////////////////////////////////////////////////////////////////////////
testReservedAttributes : function () {
var doc = { "_id" : "foo", "_rev": "99", "_key" : "meow", "_from" : "33", "_to": "99", "_test" : false };
var d1 = c.save(doc);
var d2 = c.document(d1._id);
assertEqual("meow", d1._key);
assertEqual("meow", d2._key);
assertEqual(cn + "/meow", d1._id);
assertEqual(cn + "/meow", d2._id);
assertEqual(d1._rev, d2._rev);
// user specified _rev value must have been ignored
assertTrue(d1._rev !== "99");
// test attributes
var i;
for (i in d2) {
if (d2.hasOwnProperty(i)) {
assertTrue(i !== "_from" && i !== "_to" && i !== "_test");
}
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief attribute name with special chars
////////////////////////////////////////////////////////////////////////////////
testSpecialAttributes : function () {
var doc = { "-meow-" : 1, "mötör" : 2, " " : 3, "\t" : 4, "\r" : 5, "\n" : 6 };
var d1 = c.save(doc);
var d2 = c.document(d1._id);
assertEqual(1, d2["-meow-"]);
assertEqual(2, d2["mötör"]);
assertEqual(3, d2[" "]);
assertEqual(4, d2["\t"]);
assertEqual(5, d2["\r"]);
assertEqual(6, d2["\n"]);
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(AttributesSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -1,347 +0,0 @@
var jsunity = require("jsunity");
var console = require("console");
var arangodb = require("org/arangodb");
var Frank = require("org/arangodb/frank").Frank;
var db = arangodb.db;
function CreateFrankSpec () {
return {
testCreationWithoutParameters: function () {
var app = new Frank(),
routingInfo = app.routingInfo;
assertEqual(routingInfo.routes.length, 0);
assertUndefined(routingInfo.urlPrefix);
},
testCreationWithUrlPrefix: function () {
var app = new Frank({urlPrefix: "/frankentest"}),
routingInfo = app.routingInfo;
assertEqual(routingInfo.routes.length, 0);
assertEqual(routingInfo.urlPrefix, "/frankentest");
},
testCreationWithTemplateCollectionIfCollectionDoesntExist: function () {
var app, routingInfo, templateCollection;
db._drop("frankentest");
app = new Frank({templateCollection: "frankentest"});
routingInfo = app.routingInfo;
templateCollection = db._collection("frankentest");
assertEqual(routingInfo.routes.length, 0);
assertNotNull(templateCollection);
assertEqual(routingInfo.templateCollection, templateCollection);
},
testCreationWithTemplateCollectionIfCollectionDoesExist: function () {
var app, routingInfo, templateCollection;
db._drop("frankentest");
db._create("frankentest");
app = new Frank({templateCollection: "frankentest"});
routingInfo = app.routingInfo;
templateCollection = db._collection("frankentest");
assertEqual(routingInfo.routes.length, 0);
assertNotNull(templateCollection);
assertEqual(routingInfo.templateCollection, templateCollection);
}
};
}
function SetRoutesFrankSpec () {
var app;
return {
setUp: function () {
app = new Frank();
},
testSettingRoutesWithoutConstraint: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.get('/simple/route', myFunc);
assertEqual(routes.length, 1);
assertEqual(routes[0].url.match, '/simple/route');
assertUndefined(routes[0].url.constraint);
},
testSettingRoutesWithConstraint: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
constraint = { test: "/[a-z]+/" };
app.get('/simple/route', { constraint: constraint }, myFunc);
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint, constraint);
},
testSetMethodToHead: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.head('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["head"]);
},
testSetMethodToGet: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.get('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["get"]);
},
testSetMethodToPost: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.post('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["post"]);
},
testSetMethodToPut: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.put('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["put"]);
},
testSetMethodToPatch: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.patch('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["patch"]);
},
testSetMethodToDelete: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.delete('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["delete"]);
},
testRefuseRoutesWithRoutesThatAreNumbers: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(2, myFunc);
} catch(e) {
error = e;
}
assertEqual(error, "URL has to be a String");
assertEqual(routes.length, 0);
},
testRefuseRoutesWithRoutesThatAreRegularExpressions: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(/[a-z]*/, myFunc);
} catch(e) {
error = e;
}
assertEqual(error, "URL has to be a String");
assertEqual(routes.length, 0);
},
testRefuseRoutesWithRoutesThatAreArrays: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(["/a", "/b"], myFunc);
} catch(e) {
error = e;
}
assertEqual(error, "URL has to be a String");
assertEqual(routes.length, 0);
},
testSettingHandlers: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.get('/simple/route', myFunc);
assertEqual(routes.length, 1);
assertEqual(routes[0].handler, myFunc);
}
};
}
function BaseMiddlewareWithoutTemplateSpec () {
var baseMiddleware, request, response, options, next;
return {
setUp: function () {
baseMiddleware = new require("org/arangodb/frank").BaseMiddleware();
request = {};
response = {};
options = {};
next = function () {};
},
testStatusFunctionAddedToResponse: function () {
baseMiddleware(request, response, options, next);
response.status(200);
assertEqual(response.responseCode, 200);
},
testSetFunctionAddedToResponse: function () {
baseMiddleware(request, response, options, next);
response.set("Content-Length", "123");
assertEqual(response.headers["content-length"], "123");
response.set("Content-Type", "text/plain");
assertEqual(response.contentType, "text/plain");
},
testSetFunctionTakingAnObjectAddedToResponse: function () {
baseMiddleware(request, response, options, next);
response.set({
"Content-Length": "123",
"Content-Type": "text/plain"
});
assertEqual(response.headers["content-length"], "123");
assertEqual(response.contentType, "text/plain");
},
testJsonFunctionAddedToResponse: function () {
var rawObject = {test: "123"};
baseMiddleware(request, response, options, next);
response.json(rawObject);
assertEqual(response.body, JSON.stringify(rawObject));
assertEqual(response.contentType, "application/json");
},
testTemplateFunctionAddedToResponse: function () {
var error;
baseMiddleware(request, response, options, next);
try {
response.render("simple/path", { username: "moonglum" });
} catch(e) {
error = e;
}
assertEqual(error, "No template collection has been provided when creating a new Frank");
},
testMiddlewareCallsTheAction: function () {
var actionWasCalled = false;
next = function () {
actionWasCalled = true;
};
baseMiddleware(request, response, options, next);
assertTrue(actionWasCalled);
}
};
}
function BaseMiddlewareWithTemplateSpec () {
var BaseMiddleware, request, response, options, next;
return {
setUp: function () {
request = {};
response = {};
options = {};
next = function () {};
BaseMiddleware = new require("org/arangodb/frank").BaseMiddleware;
},
testRenderingATemplate: function () {
var myCollection, middleware;
db._drop("templateTest");
myCollection = db._create("templateTest");
myCollection.save({
path: "simple/path",
content: "hallo <%= username %>",
contentType: "text/plain",
templateLanguage: "underscore"
});
middleware = new BaseMiddleware(myCollection);
middleware(request, response, options, next);
response.render("simple/path", { username: "moonglum" });
assertEqual(response.body, "hallo moonglum");
assertEqual(response.contentType, "text/plain");
},
testRenderingATemplateWithAnUnknownTemplateEngine: function () {
var myCollection, error, middleware;
db._drop("templateTest");
myCollection = db._create("templateTest");
myCollection.save({
path: "simple/path",
content: "hallo <%= username %>",
contentType: "text/plain",
templateLanguage: "pirateEngine"
});
middleware = new BaseMiddleware(myCollection);
middleware(request, response, options, next);
try {
response.render("simple/path", { username: "moonglum" });
} catch(e) {
error = e;
}
assertEqual(error, "Unknown template language 'pirateEngine'");
},
testRenderingATemplateWithAnNotExistingTemplate: function () {
var myCollection, error, middleware;
db._drop("templateTest");
myCollection = db._create("templateTest");
middleware = new BaseMiddleware(myCollection);
middleware(request, response, options, next);
try {
response.render("simple/path", { username: "moonglum" });
} catch(e) {
error = e;
}
assertEqual(error, "Template 'simple/path' does not exist");
}
};
}
jsunity.run(CreateFrankSpec);
jsunity.run(SetRoutesFrankSpec);
jsunity.run(BaseMiddlewareWithoutTemplateSpec);
jsunity.run(BaseMiddlewareWithTemplateSpec);
return jsunity.done();

View File

@ -0,0 +1,612 @@
require("internal").flushModuleCache();
var jsunity = require("jsunity"),
console = require("console"),
arangodb = require("org/arangodb"),
FuxxApplication = require("org/arangodb/fuxx").FuxxApplication,
db = arangodb.db;
function CreateFuxxApplicationSpec () {
return {
testCreationWithoutParameters: function () {
var app = new FuxxApplication(),
routingInfo = app.routingInfo;
assertEqual(routingInfo.routes.length, 0);
assertUndefined(routingInfo.urlPrefix);
},
testCreationWithURLPrefix: function () {
var app = new FuxxApplication({urlPrefix: "/wiese"}),
routingInfo = app.routingInfo;
assertEqual(routingInfo.routes.length, 0);
assertEqual(routingInfo.urlPrefix, "/wiese");
},
testCreationWithTemplateCollectionIfCollectionDoesntExist: function () {
var app, routingInfo, templateCollection;
db._drop("wiese");
app = new FuxxApplication({templateCollection: "wiese"});
routingInfo = app.routingInfo;
templateCollection = db._collection("wiese");
assertEqual(routingInfo.routes.length, 0);
assertNotNull(templateCollection);
assertEqual(routingInfo.templateCollection, templateCollection);
},
testCreationWithTemplateCollectionIfCollectionDoesExist: function () {
var app, routingInfo, templateCollection;
db._drop("wiese");
db._create("wiese");
app = new FuxxApplication({templateCollection: "wiese"});
routingInfo = app.routingInfo;
templateCollection = db._collection("wiese");
assertEqual(routingInfo.routes.length, 0);
assertNotNull(templateCollection);
assertEqual(routingInfo.templateCollection, templateCollection);
},
testAdditionOfBaseMiddlewareInRoutingInfo: function () {
var app = new FuxxApplication(),
routingInfo = app.routingInfo,
hopefully_base = routingInfo.middleware[0];
assertEqual(routingInfo.middleware.length, 1);
assertEqual(hopefully_base.url.match, "/*");
}
};
}
function SetRoutesFuxxApplicationSpec () {
var app;
return {
setUp: function () {
app = new FuxxApplication();
},
testSettingRoutesWithoutConstraint: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.get('/simple/route', myFunc);
assertEqual(routes.length, 1);
assertEqual(routes[0].url.match, '/simple/route');
assertUndefined(routes[0].url.constraint);
},
testSettingRoutesWithConstraint: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
constraint = { test: "/[a-z]+/" };
app.get('/simple/route', { constraint: constraint }, myFunc);
assertEqual(routes.length, 1);
assertEqual(routes[0].url.constraint, constraint);
},
testSetMethodToHead: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.head('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["head"]);
},
testSetMethodToGet: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.get('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["get"]);
},
testSetMethodToPost: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.post('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["post"]);
},
testSetMethodToPut: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.put('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["put"]);
},
testSetMethodToPatch: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.patch('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["patch"]);
},
testSetMethodToDelete: function () {
var myFunc = function () {},
routes = app.routingInfo.routes;
app.delete('/simple/route', myFunc);
assertEqual(routes[0].url.methods, ["delete"]);
},
testRefuseRoutesWithRoutesThatAreNumbers: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(2, myFunc);
} catch(e) {
error = e;
}
assertEqual(error, "URL has to be a String");
assertEqual(routes.length, 0);
},
testRefuseRoutesWithRoutesThatAreRegularExpressions: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(/[a-z]*/, myFunc);
} catch(e) {
error = e;
}
assertEqual(error, "URL has to be a String");
assertEqual(routes.length, 0);
},
testRefuseRoutesWithRoutesThatAreArrays: function () {
var myFunc = function () {},
routes = app.routingInfo.routes,
error;
try {
app.get(["/a", "/b"], myFunc);
} catch(e) {
error = e;
}
assertEqual(error, "URL has to be a String");
assertEqual(routes.length, 0);
},
testSettingHandlers: function () {
var myFunc = function a (b, c) { return b + c;},
myFuncString = "function a(b, c) { return b + c;}",
routes = app.routingInfo.routes,
action;
app.get('/simple/route', myFunc);
action = routes[0].action;
assertEqual(routes.length, 1);
assertEqual(action.callback, myFuncString);
}
};
}
function AddMidlewareFuxxApplicationSpec () {
var app;
return {
setUp: function () {
app = new FuxxApplication();
},
testAddABeforeMiddlewareForAllRoutes: function () {
var a = false,
b = false,
myFunc = function (req, res) { a = (req > res); },
myNext = function () { b = a; },
middleware = app.routingInfo.middleware,
resultingCallback;
app.before(myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/*');
resultingCallback = middleware[1].action.callback;
resultingCallback(2, 1, undefined, myNext);
assertTrue(a);
assertTrue(b);
},
testAddABeforeMiddlewareForCertainRoutes: function () {
var a = false,
b = false,
myFunc = function (req, res) { a = (req > res); },
myNext = function () { b = a; },
middleware = app.routingInfo.middleware,
resultingCallback;
app.before('/fancy/path', myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/fancy/path');
resultingCallback = middleware[1].action.callback;
resultingCallback(2, 1, undefined, myNext);
assertTrue(a);
assertTrue(b);
},
testAddAfterMiddlewareForAllRoutes: function () {
var a = false,
b = false,
myNext = function () { a = true; },
myFunc = function (req, res) { b = a && (req > res); },
middleware = app.routingInfo.middleware,
resultingCallback;
app.after(myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/*');
resultingCallback = middleware[1].action.callback;
resultingCallback(2, 1, undefined, myNext);
assertTrue(a);
assertTrue(b);
},
testAddAfterMiddlewareForCertainRoutes: function () {
var a = false,
b = false,
myNext = function () { a = true; },
myFunc = function (req, res) { b = a && (req > res); },
middleware = app.routingInfo.middleware,
resultingCallback;
app.after('/my/way', myFunc);
assertEqual(middleware.length, 2);
assertEqual(middleware[1].url.match, '/my/way');
resultingCallback = middleware[1].action.callback;
resultingCallback(2, 1, undefined, myNext);
assertTrue(a);
assertTrue(b);
},
testAddTheFormatMiddlewareUsingTheShortform: function () {
// I wish I could mock like in Ruby :( This test is not really a test.
app.accepts(["json"], "json");
assertEqual(app.routingInfo.middleware.length, 2);
assertEqual(app.routingInfo.middleware[1].url.match, '/*');
}
};
}
function BaseMiddlewareWithoutTemplateSpec () {
var baseMiddleware, request, response, options, next;
return {
setUp: function () {
baseMiddleware = require("org/arangodb/fuxx").BaseMiddleware();
request = {};
response = {};
options = {};
next = function () {};
},
testStatusFunctionAddedToResponse: function () {
baseMiddleware(request, response, options, next);
response.status(200);
assertEqual(response.responseCode, 200);
},
testSetFunctionAddedToResponse: function () {
baseMiddleware(request, response, options, next);
response.set("Content-Length", "123");
assertEqual(response.headers["content-length"], "123");
response.set("Content-Type", "text/plain");
assertEqual(response.contentType, "text/plain");
},
testSetFunctionTakingAnObjectAddedToResponse: function () {
baseMiddleware(request, response, options, next);
response.set({
"Content-Length": "123",
"Content-Type": "text/plain"
});
assertEqual(response.headers["content-length"], "123");
assertEqual(response.contentType, "text/plain");
},
testJsonFunctionAddedToResponse: function () {
var rawObject = {test: "123"};
baseMiddleware(request, response, options, next);
response.json(rawObject);
assertEqual(response.body, JSON.stringify(rawObject));
assertEqual(response.contentType, "application/json");
},
testTemplateFunctionAddedToResponse: function () {
var error;
baseMiddleware(request, response, options, next);
try {
response.render("simple/path", { username: "moonglum" });
} catch(e) {
error = e;
}
assertEqual(error, "No template collection has been provided when creating a new FuxxApplication");
},
testMiddlewareCallsTheAction: function () {
var actionWasCalled = false;
next = function () {
actionWasCalled = true;
};
baseMiddleware(request, response, options, next);
assertTrue(actionWasCalled);
}
};
}
function BaseMiddlewareWithTemplateSpec () {
var BaseMiddleware, request, response, options, next;
return {
setUp: function () {
request = {};
response = {};
options = {};
next = function () {};
BaseMiddleware = require("org/arangodb/fuxx").BaseMiddleware;
},
testRenderingATemplate: function () {
var myCollection, middleware;
db._drop("templateTest");
myCollection = db._create("templateTest");
myCollection.save({
path: "simple/path",
content: "hallo <%= username %>",
contentType: "text/plain",
templateLanguage: "underscore"
});
middleware = new BaseMiddleware(myCollection);
middleware(request, response, options, next);
response.render("simple/path", { username: "moonglum" });
assertEqual(response.body, "hallo moonglum");
assertEqual(response.contentType, "text/plain");
},
testRenderingATemplateWithAnUnknownTemplateEngine: function () {
var myCollection, error, middleware;
db._drop("templateTest");
myCollection = db._create("templateTest");
myCollection.save({
path: "simple/path",
content: "hallo <%= username %>",
contentType: "text/plain",
templateLanguage: "pirateEngine"
});
middleware = new BaseMiddleware(myCollection);
middleware(request, response, options, next);
try {
response.render("simple/path", { username: "moonglum" });
} catch(e) {
error = e;
}
assertEqual(error, "Unknown template language 'pirateEngine'");
},
testRenderingATemplateWithAnNotExistingTemplate: function () {
var myCollection, error, middleware;
db._drop("templateTest");
myCollection = db._create("templateTest");
middleware = new BaseMiddleware(myCollection);
middleware(request, response, options, next);
try {
response.render("simple/path", { username: "moonglum" });
} catch(e) {
error = e;
}
assertEqual(error, "Template 'simple/path' does not exist");
}
};
}
function ViewHelperSpec () {
var app, Middleware, request, response, options, next;
return {
setUp: function () {
app = new FuxxApplication();
Middleware = require('org/arangodb/fuxx').BaseMiddleware;
request = {};
response = {};
options = {};
next = function () {};
},
testDefiningAViewHelper: function () {
var my_func = function () {};
app.helper("testHelper", my_func);
assertEqual(app.helperCollection["testHelper"], my_func);
},
testUsingTheHelperInATemplate: function () {
var a = false;
db._drop("templateTest");
myCollection = db._create("templateTest");
myCollection.save({
path: "simple/path",
content: "hallo <%= testHelper() %>",
contentType: "text/plain",
templateLanguage: "underscore"
});
middleware = new Middleware(myCollection, {
testHelper: function() { a = true }
});
middleware(request, response, options, next);
assertFalse(a);
response.render("simple/path", {});
assertTrue(a);
}
};
}
function FormatMiddlewareSpec () {
var Middleware, middleware, request, response, options, next;
return {
setUp: function () {
Middleware = require('org/arangodb/fuxx').FormatMiddleware;
request = {};
response = {};
options = {};
next = function () {};
middleware = new Middleware(["json"]);
},
testChangesTheURLAccordingly: function () {
request = { path: "test/1.json", headers: {} };
middleware(request, response, options, next);
assertEqual(request.path, "test/1");
},
testRefusesFormatsThatHaveNotBeenAllowed: function () {
var nextCalled = false,
next = function () { nextCalled = true };
middleware = new Middleware(["json"]);
request = { path: "test/1.html", headers: {} };
middleware(request, response, options, next);
assertEqual(response.responseCode, 406);
assertEqual(response.body, "Format 'html' is not allowed.");
assertFalse(nextCalled);
},
testRefuseContradictingURLAndResponseType: function () {
var nextCalled = false,
next = function () { nextCalled = true };
request = { path: "test/1.json", headers: {"accept": "text/html"} };
middleware = new Middleware(["json"]);
middleware(request, response, options, next);
assertEqual(response.responseCode, 406);
assertEqual(response.body, "Contradiction between Accept Header and URL.");
assertFalse(nextCalled);
},
testRefuseMissingBothURLAndResponseTypeWhenNoDefaultWasGiven: function () {
var nextCalled = false,
next = function () { nextCalled = true };
request = { path: "test/1", headers: {} };
middleware = new Middleware(["json"]);
middleware(request, response, options, next);
assertEqual(response.responseCode, 406);
assertEqual(response.body, "Format 'undefined' is not allowed.");
assertFalse(nextCalled);
},
testFallBackToDefaultWhenMissingBothURLAndResponseType: function () {
request = { path: "test/1", headers: {} };
middleware = new Middleware(["json"], "json");
middleware(request, response, options, next);
assertEqual(request.format, "json");
assertEqual(response.contentType, "application/json");
},
// JSON
testSettingTheFormatAttributeAndResponseTypeForJsonViaURL: function () {
request = { path: "test/1.json", headers: {} };
middleware = new Middleware(["json"]);
middleware(request, response, options, next);
assertEqual(request.format, "json");
assertEqual(response.contentType, "application/json");
},
testSettingTheFormatAttributeAndResponseTypeForJsonViaAcceptHeader: function () {
request = { path: "test/1", headers: {"accept": "application/json"} };
middleware = new Middleware(["json"]);
middleware(request, response, options, next);
assertEqual(request.format, "json");
assertEqual(response.contentType, "application/json");
},
// HTML
testSettingTheFormatAttributeAndResponseTypeForHtmlViaURL: function () {
request = { path: "test/1.html", headers: {} };
middleware = new Middleware(["html"]);
middleware(request, response, options, next);
assertEqual(request.format, "html");
assertEqual(response.contentType, "text/html");
},
testSettingTheFormatAttributeAndResponseTypeForHtmlViaAcceptHeader: function () {
request = { path: "test/1", headers: {"accept": "text/html"} };
middleware = new Middleware(["html"]);
middleware(request, response, options, next);
assertEqual(request.format, "html");
assertEqual(response.contentType, "text/html");
},
// TXT
testSettingTheFormatAttributeAndResponseTypeForTxtViaURL: function () {
request = { path: "test/1.txt", headers: {} };
middleware = new Middleware(["txt"]);
middleware(request, response, options, next);
assertEqual(request.format, "txt");
assertEqual(response.contentType, "text/plain");
},
testSettingTheFormatAttributeAndResponseTypeForTxtViaAcceptHeader: function () {
request = { path: "test/1", headers: {"accept": "text/plain"} };
middleware = new Middleware(["txt"]);
middleware(request, response, options, next);
assertEqual(request.format, "txt");
assertEqual(response.contentType, "text/plain");
}
};
}
jsunity.run(CreateFuxxApplicationSpec);
jsunity.run(SetRoutesFuxxApplicationSpec);
jsunity.run(AddMidlewareFuxxApplicationSpec);
jsunity.run(BaseMiddlewareWithoutTemplateSpec);
jsunity.run(BaseMiddlewareWithTemplateSpec);
jsunity.run(ViewHelperSpec);
jsunity.run(FormatMiddlewareSpec);
return jsunity.done();

View File

@ -190,6 +190,39 @@ function StatementSuite () {
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////
testExplainNoBindError : function () {
var st = new ArangoStatement(db, { query : "for i in [ 1 ] return @f" });
try {
st.explain();
}
catch (e) {
assertEqual(1551, e.errorNum);
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////
testExplainWithBind : function () {
var st = new ArangoStatement(db, { query : "for i in [ 1 ] return @f", bindVars: { f : 1 } });
var result = st.explain();
assertEqual(2, result.length);
assertEqual(1, result[0]["id"]);
assertEqual(1, result[0]["loopLevel"]);
assertEqual("for", result[0]["type"]);
assertEqual(2, result[1]["id"]);
assertEqual(1, result[1]["loopLevel"]);
assertEqual("return", result[1]["type"]);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test explain method
////////////////////////////////////////////////////////////////////////////////

View File

@ -62,7 +62,7 @@ ArangoStatement.prototype.parse = function () {
////////////////////////////////////////////////////////////////////////////////
ArangoStatement.prototype.explain = function () {
return EXPLAIN(this._query);
return EXPLAIN(this._query, this._bindVars);
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -89,7 +89,6 @@ typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@ -213,11 +212,6 @@ typedef void* yyscan_t;
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#define EOB_ACT_CONTINUE_SCAN 0
#define EOB_ACT_END_OF_FILE 1
#define EOB_ACT_LAST_MATCH 2
@ -240,6 +234,11 @@ typedef size_t yy_size_t;
#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
@ -257,7 +256,7 @@ struct yy_buffer_state
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
yy_size_t yy_n_chars;
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
@ -336,7 +335,7 @@ static void tri_jsp__init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscann
YY_BUFFER_STATE tri_jsp__scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
YY_BUFFER_STATE tri_jsp__scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
void *tri_jsp_alloc (yy_size_t ,yyscan_t yyscanner );
void *tri_jsp_realloc (void *,yy_size_t ,yyscan_t yyscanner );
@ -387,7 +386,7 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
*/
#define YY_DO_BEFORE_ACTION \
yyg->yytext_ptr = yy_bp; \
yyleng = (yy_size_t) (yy_cp - yy_bp); \
yyleng = (size_t) (yy_cp - yy_bp); \
yyg->yy_hold_char = *yy_cp; \
*yy_cp = '\0'; \
yyg->yy_c_buf_p = yy_cp;
@ -566,8 +565,8 @@ struct yyguts_t
size_t yy_buffer_stack_max; /**< capacity of stack. */
YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
char yy_hold_char;
yy_size_t yy_n_chars;
yy_size_t yyleng_r;
int yy_n_chars;
int yyleng_r;
char *yy_c_buf_p;
int yy_init;
int yy_start;
@ -614,7 +613,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner );
void tri_jsp_set_out (FILE * out_str ,yyscan_t yyscanner );
yy_size_t tri_jsp_get_leng (yyscan_t yyscanner );
int tri_jsp_get_leng (yyscan_t yyscanner );
char *tri_jsp_get_text (yyscan_t yyscanner );
@ -673,7 +672,7 @@ static int input (yyscan_t yyscanner );
if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
{ \
int c = '*'; \
yy_size_t n; \
int n; \
for ( n = 0; n < max_size && \
(c = getc( yyin )) != EOF && c != '\n'; ++n ) \
buf[n] = (char) c; \
@ -1129,7 +1128,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
else
{
yy_size_t num_to_read =
int num_to_read =
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
while ( num_to_read <= 0 )
@ -1143,7 +1142,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
if ( b->yy_is_our_buffer )
{
yy_size_t new_size = b->yy_buf_size * 2;
int new_size = b->yy_buf_size * 2;
if ( new_size <= 0 )
b->yy_buf_size += b->yy_buf_size / 8;
@ -1174,7 +1173,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
/* Read in more data. */
YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
yyg->yy_n_chars, num_to_read );
yyg->yy_n_chars, (int) num_to_read );
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
}
@ -1299,7 +1298,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
else
{ /* need more input */
yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
++yyg->yy_c_buf_p;
switch ( yy_get_next_buffer( yyscanner ) )
@ -1323,7 +1322,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
case EOB_ACT_END_OF_FILE:
{
if ( tri_jsp_wrap(yyscanner ) )
return 0;
return EOF;
if ( ! yyg->yy_did_buffer_switch_on_eof )
YY_NEW_FILE;
@ -1585,7 +1584,7 @@ void tri_jsp_pop_buffer_state (yyscan_t yyscanner)
*/
static void tri_jsp_ensure_buffer_stack (yyscan_t yyscanner)
{
yy_size_t num_to_alloc;
int num_to_alloc;
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
if (!yyg->yy_buffer_stack) {
@ -1683,11 +1682,12 @@ YY_BUFFER_STATE tri_jsp__scan_string (yyconst char * yystr , yyscan_t yyscanner)
* @param yyscanner The scanner object.
* @return the newly allocated buffer state object.
*/
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
{
YY_BUFFER_STATE b;
char *buf;
yy_size_t n, i;
yy_size_t n;
int i;
/* Get memory for full buffer, including space for trailing EOB's. */
n = _yybytes_len + 2;
@ -1797,7 +1797,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner)
/** Get the length of the current token.
* @param yyscanner The scanner object.
*/
yy_size_t tri_jsp_get_leng (yyscan_t yyscanner)
int tri_jsp_get_leng (yyscan_t yyscanner)
{
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
return yyleng;
@ -2184,7 +2184,7 @@ static TRI_json_t* ParseArray (yyscan_t scanner) {
// do proper unescaping
name = TRI_UnescapeUtf8StringZ(yyextra._memoryZone, yytext + 1, nameLen, &outLength);
nameLen = outLength;
}
else if (c == STRING_CONSTANT_ASCII) {
// ASCII-only attribute name

View File

@ -286,7 +286,7 @@ static TRI_json_t* ParseArray (yyscan_t scanner) {
// do proper unescaping
name = TRI_UnescapeUtf8StringZ(yyextra._memoryZone, yytext + 1, nameLen, &outLength);
nameLen = outLength;
}
else if (c == STRING_CONSTANT_ASCII) {
// ASCII-only attribute name

View File

@ -310,6 +310,8 @@ static int WeightShapeType (TRI_shape_type_t type) {
case TRI_SHAPE_HOMOGENEOUS_LIST: return 900;
}
LOG_ERROR("invalid shape type: %d\n", (int) type);
assert(false);
return 0;
}
@ -780,8 +782,8 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, T
char* ptr;
// sanity checks
assert(json->_type == TRI_JSON_ARRAY);
assert(json->_value._objects._length % 2 == 0);
TRI_ASSERT_DEBUG(json->_type == TRI_JSON_ARRAY);
TRI_ASSERT_DEBUG(json->_value._objects._length % 2 == 0);
// number of attributes
n = json->_value._objects._length / 2;
@ -805,6 +807,17 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, T
key = TRI_AtVector(&json->_value._objects, 2 * i);
val = TRI_AtVector(&json->_value._objects, 2 * i + 1);
TRI_ASSERT_DEBUG(key != NULL);
TRI_ASSERT_DEBUG(val != NULL);
if (key->_value._string.data == NULL ||
key->_value._string.length == 1 ||
key->_value._string.data[0] == '_') {
// empty or reserved attribute name
p--;
continue;
}
// first find an identifier for the name
p->_aid = shaper->findAttributeName(shaper, key->_value._string.data);
@ -841,6 +854,9 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, T
// add variable offset table size
total += (v + 1) * sizeof(TRI_shape_size_t);
// now adjust n because we might have excluded empty attributes
n = f + v;
// now sort the shape entries
TRI_SortShapeValues(values, n);

View File

@ -289,8 +289,6 @@ static bool FillShapeValueList (TRI_shaper_t* shaper,
return false;
}
memset(values, 0, sizeof(TRI_shape_value_t) * n);
total = 0;
e = values + n;
@ -592,7 +590,7 @@ static bool FillShapeValueArray (TRI_shaper_t* shaper,
// first find an identifier for the name
TRI_Utf8ValueNFC keyStr(TRI_UNKNOWN_MEM_ZONE, key);
if (*keyStr == 0) {
if (*keyStr == 0 || keyStr.length() == 0) {
--p;
continue;
}
@ -789,13 +787,12 @@ static bool FillShapeValueJson (TRI_shaper_t* shaper,
return FillShapeValueNull(shaper, dst);
}
}
seenObjects.push_back(o);
}
else {
seenHashes.insert(hash);
seenObjects.push_back(o);
}
seenObjects.push_back(o);
}
if (json->IsNull()) {
@ -1439,6 +1436,7 @@ TRI_json_t* TRI_ObjectToJson (v8::Handle<v8::Value> parameter) {
const uint32_t n = arrayParameter->Length();
TRI_json_t* listJson = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, (const size_t) n);
if (listJson != 0) {
for (uint32_t j = 0; j < n; ++j) {
v8::Handle<v8::Value> item = arrayParameter->Get(j);
@ -1458,9 +1456,10 @@ TRI_json_t* TRI_ObjectToJson (v8::Handle<v8::Value> parameter) {
const uint32_t n = names->Length();
TRI_json_t* arrayJson = TRI_CreateArray2Json(TRI_UNKNOWN_MEM_ZONE, (const size_t) n);
if (arrayJson != 0) {
for (uint32_t j = 0; j < n; ++j) {
v8::Handle<v8::Value> key = names->Get(j);
v8::Handle<v8::Value> key = names->Get(j);
v8::Handle<v8::Value> item = arrayParameter->Get(key);
TRI_json_t* result = TRI_ObjectToJson(item);