mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
3c2f88d6ab
137
CHANGELOG
137
CHANGELOG
|
@ -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
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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**
|
|
@ -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
|
|
@ -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 \
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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>());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -483,7 +483,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var quoteSingleJsonCharacter = function (c) {
|
||||
if (characterQuoteCache.hasOwnProperty[c]) {
|
||||
if (characterQuoteCache.hasOwnProperty(c)) {
|
||||
return characterQuoteCache[c];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -249,7 +249,7 @@ var documentView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
escaped: function (value) {
|
||||
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "&");
|
||||
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
},
|
||||
|
||||
updateLocalDocumentStorage: function () {
|
||||
|
|
|
@ -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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "&");
|
||||
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -483,7 +483,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var quoteSingleJsonCharacter = function (c) {
|
||||
if (characterQuoteCache.hasOwnProperty[c]) {
|
||||
if (characterQuoteCache.hasOwnProperty(c)) {
|
||||
return characterQuoteCache[c];
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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:
|
|
@ -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();
|
|
@ -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();
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -62,7 +62,7 @@ ArangoStatement.prototype.parse = function () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoStatement.prototype.explain = function () {
|
||||
return EXPLAIN(this._query);
|
||||
return EXPLAIN(this._query, this._bindVars);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue