Storing Datasets
Modifying Datasets - Single Modification
Now we are ready to modify some data. To do this, we submit an http
request with request method PUT
(update), POST
(insert), or DELETE
(delete). These requests may specify either a single record, or an array of one or more records.
Note: For clients written in Adobe Flex, the framework you use may force you to use only GET
or POST
. In this case you may specify an override method (POST
, POST
, DELETE
) to Jarvis by passing the _method
parameter as a CGI parameter in the GET
or POST
request.
In return you will receive a response body containing a matching single record, or a matching array of one or more records. Let us consider a practical case. Here is a POST
request body to insert a record into the boat
table in the demo database in the single record (non-array) case.
The Content-Type must be application/json
or text/json
for JSON, and application/xml
or `text/xml for XML.
In JSON the request is as follows:
(Request-Line) POST /jarvis-agent/demo/boat HTTP/1.1
Content-Type application/json; charset=UTF-8
{
"id" : 0,
"name" : "New Boat Name",
"class" : "Makkleson",
"registration_num" : 0,
"_record_id" : 1007
}
Corresponding request in XML:
(Request-Line) POST /jarvis-agent/demo/boat HTTP/1.1
Content-Type application/xml; charset=UTF-8
<request>
<id>0</id>
<name>New Boat Name</name>
<class>Makkleson</class>
<registration_num>0</registration_num>
<_record_id>12</_record_id>
</request>
The SQL in the <insert>
statement is:
...
<insert returning="yes">
INSERT INTO boat (name, registration_num, class, owner, description, change_user, change_date)
VALUES (
{$name},
NULLIF ({$registration_num}, 0),
NULLIF (BTRIM ({$class}), ''),
NULLIF (BTRIM ({$owner}), ''),
NULLIF (BTRIM ({$description}), ''),
{$__username}, now()
) RETURNING {$_record_id}::integer as _record_id, id;
</insert>
...
The user parameters in the JSON record are substituted into the SQL, along with any server side default_parameters
values, and any secure parameters such as {$__username}
. The statement is prepared and executed. Because the attribute returning
is defined as yes
, then the insert statement results are fetched and returned in a returning
parameter of the resulting JSON.
The response is correspondingly a single record, e.g response in JSON:
(Status-Line) HTTP/1.1 200 OK
Content-Type application/json; charset=ISO-8859-1
{
"success" : 1,
"returning" : [
{
"_record_id" : "1009",
"id" : "16"
}
],
"modified" : 1
}
Response in XML:
<?xml version="1.0" encoding="iso-8859-1" ?>
<?meta name="GENERATOR" content="XML::Smart/1.6.9 Perl/5.010000 [linux]" ?>
<response success="1" modified="1">
<returning _record_id="1206" id="639"/>
</response>
In either case, the top-level attributes of the returned object are as follows if the http_code_on_store_error
flag at the Jarvis -> App configuration level is set to no
(or not specified).
Attribute | Notes |
---|---|
success |
Overall success of the modification request. success = 1 if the update succeeded or 0 if the update failed.For array modifications, success = 1 only if all array modifications succeeded. Otherwise success = 0 indicates one or more rows failed. |
modified |
The total of records modified. For an array modification, the top level modified value is the sum of all of the modified values in all of the row attributes.This attribute is present only if success = 1 . |
message |
This is present only if one of the updates failed. For a non-array modification this is the error message for the failed update. For an array modification, this is a copy of the error message for the first failed update in the row sub-array. i.e. for a failed array modification, the first error message is always present twice in the returned result. |
returning |
This is present at the top level only for a non-array modification where the returning clause is specified in the dataset definition, and where the SQL operation did in fact return one or more rows. The attributes of the returning entries are those returned columns specified in the SQL definition.The most common use of the returning clause is returning the auto-generated serial ID value for an inserted row. For an array modification, the returning elements are found within each of the row update return results.Returning is an array (JSON) or repeatable element (XML), because it is possible for a single insert/update statement to return more than one row. Under PostgreSQL (and very likely other drivers) an error will occur if you specify returning="yes" but then do not add a RETURNING clause to the SQL.That SQLite does not support the RETURNING clause. However, under SQLite if you specify returning="yes then Jarvis will automatically use the last_insert_rowid() mechanism and will return it as a column named id . |
row |
For an array modification request, the one or more row elements in the result returns the results of each individual modification request in the array.This attribute is present only if success = 1 . |
If the http_code_on_store_error
flag is set to yes
, only successful store actions return a 200 success. Any failure received from the database (e.g. due to a constraint error) will cause Jarvis to generate a HTTP 500 error back.
Modifying Datasets - Array of Modifications
The array update case allows multiple transactions of the same type to be performed at once, for better throughput. Only transactions of the same type (i.e. either UPDATE
or INSERT
or DELETE
) may be performed within a single Jarvis http request. If you require a mix of transaction types, you must send separate requests for each type.
If your database supports the MERGE
statement (e.g. recent Oracle versions, or PostgreSQL, or SQL Server 2008) then you could write your <update>
SQL statement in your dataset definition file to be a combined insert/update statement. Alternatively you could write a stored procedure to do the same thing.
For SQL Server, see: http://weblogs.sqlteam.com/mladenp/archive/2007/07/30/60273.aspx
Request Format
The format of the array modification request is simply an array of single modifications.
Note: An array with one element (an array modification) will generate a different response from a single modification (not in an array).
Here is a request updating one toggle to true
and another toggle to false
. Request in JSON:
[
{ "key": "music", "name": "Reprints" , "toggle": true },
{ "key": "music", "name": "Second Hand", "toggle": false }
]
In XML an parallel structure with <request>
and <row>
tags.
Note: The parameters may be given as attributes OR elements. E.g. the following two XML requests will give identical results.
<request>
<row key="music" name="Reprints" toggle="true"/>
<row key="music" name="Second Hand" toggle="false"/>
</request>
.. or ..
<request>
<row>
<key>music</key>
<name>Reprints</name>
<toggle>true</toggle>
</row>
<row>
<key>music</key>
<name>Second Hand</name>
<toggle>false</toggle>
</row>
</request>
The response in JSON format might be:
{
"success" : 1,
"row" : [
{
"success" : 1,
"modified" : 1
},
{
"success" : 1,
"modified" : 1
}
],
"modified" : 2
}
Or in XML the response might be:
<?xml version="1.0" encoding="iso-8859-1" ?>
<?meta name="GENERATOR" content="XML::Smart/1.6.9 Perl/5.010000 [linux]" ?>
<response success="1" modified="2">
<results>
<row modified="1" success="1"/>
<row modified="1" success="1"/>
</results>
</response>
Note: The preceding example does not show the optional returning clause. The attributes of each returned row
entry are as follows.
Attribute | Notes |
---|---|
success |
Success result of this individual modification. Value 0 (success) or 1 (failed). In the case of a single failure, if error_response_format is set to yes , then a HTTP 500 is sent back to the client. |
modified |
Number of rows affected by this individual modification. |
message |
If not success, this is the error message for this individual modification. |
returning |
Returned information. Only present if returning = "yes" was specified for this modification type in the dataset definition, and if the modification actually returned one or more rows. Elements of the returning clause are as specified in the SQL. |
Modifying Datasets - Array of Mixed Modifications
In the previous section describing array modifications, all of the changes in the transaction needed to be of a single type – e.g. all update
, all insert
, all delete
, etc. This is according to the RESTful standard, which requires the method
to be specified at the top level of the request.
However, in practical terms, it is often desirable to perform a single transaction which consists of a mixture of insert/update/delete on the same dataset. Jarvis does support that, with the MIXED
method request.
Simply specify MIXED
as the request method. Then, for each row, specify the _ttype
parameter as one of the per-row attributes or elements. E.g.
<request>
<row>
<_ttype="update" key="music" name="Reprints" toggle="true"/>
</row>
<row>
<_ttype="insert" key="music" name="Second Hand" toggle="false"/>
</row>
<row>
<_ttype="delete" key="music" name="Underfunded"/>
</row>
</request>
All of these modifications will be performed within a single begin/end transaction, including (if defined) any before and/or after statements as described subsequently.
Before & After Statements
The before
and after
elements in a dataset allow you to specify optional SQL to be executed at the beginning and end of a modification transaction (either array or single). This allows you to perform the equivalent of Pre-Commit
triggers in databases which do not otherwise support them.
When performing array modifications, all changes are performed within a bounding begin/end transaction. The actual sequence for single http/s modification request is as follows.
- Begin transaction.
- Perform
before
SQL (if configured for this dataset). - Perform the one single or one-or-more array modifications in the request body.
- Perform
after
SQL (if configured for this dataset). - Commit transaction.
An error at any stage will cause the entire transaction to be rolled back.
Note: Any before
or after
statements may contain references to:
- Secure Dataset Parameters (such as
{__username}
and{__group_list}
. - Indexed RESTful arguments as specified in the URL e.g.
{1}
or{2}
. - Named RESTful arguments assigned by a
route
rule (see therouter
configuration).
However, before
and after
statements naturally do not have access to any of the per-row parameters in the request body.
Note: The before
and after
statements are not invoked for select
operations, only for modification operations.
Transaction and Rollback
It is important to note:
- For
fetch
statements in a dataset (i.e. the<select>
attributes), no explicit transaction is performed. - For
store
statements in a dataset (i.e.<insert>
/<update>
/<delete>
attributes), an outer transaction is automatically applied.
Note: This is not based on the textual content of the SQL, but on which named attribute tag is being executed.
This means that if your <select>
attribute SQL performs multiple data modifications as a side-effect, then a failure in a later step will not rollback any earlier changes. If you require a transaction around the modifications, then either define it as an <insert>
/<update>
/<delete>
and use the appropriate REST action to invoke it, or else implement your own transaction within your SQL body.
Here is an example of a case where <insert>
element SQL was used with an array of two new rows being requested. In this case, one failed. All changes are rolled back:
{
"success" : 0,
"message" : "ERROR: duplicate key violates unique constraint \"boat_pkey\""
}
Noting:
- Even though the first modification succeeded, it was rolled back.
- The first failing error message is shown.
- The
row
elements andmodified
keys are not present.
Note: SSAS Data Pump datasets support only the fetch
mechanism. There is no writeback.