Copyright © W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
This document defines APIs for storing and retrieving ordered key-value pairs in a transactional database.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This document is the 3 November 2009 Editor’s Draft of the WebSimpleDB API specification. If you wish to make comments regarding this document, please send them to public-webapps@w3.org (subscribe, archives) with “[WebSimpleDB]” at the start of the subject line, or submit them using our public bug database.
The latest stable version of the editor's draft of this specification is always available on the W3C CVS server. Change tracking for this document is available at the following location:
The Web Applications Working Group, is the W3C Working Group responsible for this specification's progress along the W3C Recommendation track.
This is one of the proposals being considered for standardization in the area of local, persistent storage in user agents.
Publication as an Editor’s Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This section is non-normative.
User agents need to store large numbers of objects locally in order to satisfy off-line data requirements of Web applications. [WebStorage] is useful for storing pairs of keys and their corresponding values. However, it does not provide in-order retrieval of keys, efficient searching over values, or storage of duplicate values for a key.
This specification provides a concrete API to perform advanced key-value data management that is at the heart of most sophisticated query processors. It does so by using transactional databases to store keys and their corresponding values (one or more per key), and providing a means of traversing keys in a deterministic order. This is often implemented through the use of persistent B-tree data structures that are considered efficient for insertion and deletion as well as in-order traversal of very large numbers of data items.
A script can efficiently find items in an object store that come closest to the required value provided the value is stored in either a primary or a secondary key. In the following example, the 'books' object store holds data about books which are stored by their 'isbn' attribute. Additionally, an index is maintained on the 'author' attribute of the objects stored in the object store. This index can be used to look up books for a given author. If an exact match is not found, the next matching author is located.
var upgrade =
function(changes, db) {
changes.createObjectStore('books', 'isbn');
changes.createIndex('BookAuthor', 'Book', 'author', Index.MANY_TO_ONE);
// now db.version === '1.0'
};
var db = new DatabaseSync('books', '1.0', 'Book store', upgrade);
var index = db.getIndex('BookAuthor', 'Book', true);
var author = ...
var matching = index.get(author);
if (matching)
report(matching.isbn, matching.name, matching.author);
else
report(null);
Here is an example of a script using this API. First, a function
prepareDatabase() is defined. This function tries to create
the database if necessary, giving it one object store called "docids" with
the primary key "id". If it is successful, or if the table
doesn't need creating, it calls the function that does the
actual work, in this case showDocCount().
var request = null;
function prepareDatabase(ready, error) {
request = new DatabaseRequest();
request.onsuccess = ready;
request.onerror = error;
var upgrade =
function(changes, db) {
changes.createObjectStore('docids', 'id');
// now db.version === '1.0'
};
request.open('documents', '1.0', 'Offline document storage', upgrade);
}
function showDocCount(db, span) {
var storeRequest = new ObjectStoreRequest(db);
storeRequest.onsuccess =
function() {
var store = storeRequest.store, total = 0;
var cursorRequest = new CursorRequest(store);
cursorRequest.onsuccess =
function() {
span.textContent = total;
};
cursorRequest.open(function(item, cursor) {
total += cursor.count;
}, Cursor.NEXT_NO_DUPLICATE);
};
storeRequest.open('docids', true);
};
prepareDatabase(function(evt) {
// got database
var span = document.getElementById('doc-count');
showDocCount(request.database, span);
}, function (evt) {
// error getting database
var error = request.error;
alert(error.message);
});Everything in this specification is normative except for diagrams, examples, notes and sections marked as being informative.
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “RECOMMENDED”, “MAY” and “OPTIONAL” in this document are to be interpreted as described in Key words for use in RFCs to Indicate Requirement Levels [RFC2119].
This specification defines one class of products:
A user agent must behave as described in this specification in order to be considered conformant.
User agents may implement algorithms given in this specification in any way desired, so long as the end result is indistinguishable from the result that would be obtained by the specification's algorithms.
A conforming WebSimpleDatabase user agent must also be a conforming implementation of the IDL fragments of this specification, as described in the “Web IDL” specification. [WEBIDL]
This specification relies on several other underlying specifications.
Function,
origin, same origin,
task, task source, and
queue a task are defined by the HTML 5
specification [HTML5].
Each origin has an associated set of databases. A database persists keys and values in an ordered manner. Each database comprises:
Each database has a name and a human readable description. All strings including the empty string are valid database names. Database names must be compared in a case-sensitive manner.
Each database also has a current version.
When a database is opened, that creates a connection. There can be multiple connections to a given database. A connection that is attempting to read a given piece of data in a database is called a reader and one that is attempting to write that piece of data is called a writer.
There is no way to enumerate the databases
available for an origin from this API.
A Database object
represents a connection to
a database.
interface Database {
readonly attribute DOMString name;
readonly attribute DOMString description;
readonly attribute DOMString version;
readonly attribute DOMStringList objectStores;
readonly attribute Transaction currentTransaction;
};namedescriptionversionDatabase object).
objectStorescurrentTransactionTransaction object
corresponding to the transaction that is currently active in the
database
connected to this object.
The version that the database was opened with is the expected
version of this Database
object. It can be the empty string, in which case there is no
expected version — any version is fine.
This API can only be used in a Worker [WebWorkers].
In the following example, we open a database synchronously.
var db = new DatabaseSync('AddressBook', '1', 'Address Book', function(txn, db) {...});
The synchronous API for databases blocks the calling thread until a
DatabaseSync object
is ready to return. Moreover, this API also throws a
DatabaseException
if an error occurs while the database is being opened.
[Constructor(in DOMString name,
in DOMString version,
in DOMString displayName,
in optional UpgradeCallback upgrade)]
interface DatabaseSync : Database {
ObjectStoreSync getObjectStore(in DOMString name,
in optional unsigned short mode)
raises DatabaseException;
IndexSync getIndex( in DOMString name,
in DOMString storeName)
raises DatabaseException;
TransactionSync transaction( in optional DOMStringList storeNames,
in optional unsigned int timeout)
raises DatabaseException;
};
The DatabaseSync constructor
takes the following arguments: a database name, a database version,
a display name, and optionally an upgrade callback and isolation level.
When this constructor is invoked, the user agent must run the
following steps:
DatabaseException
exception with the code of that error and abort these steps.
When the user agent is to perform database opening steps, with a database name, a database version, a display name, and optionally an upgrade callback, it must perform the following steps with all but the first two and the last steps being run atomically:
INVALID_STATE_ERR.
DatabaseSync object
representing a connection
to db.
Database object
representing a connection
to db.
TIMEOUT_ERR.
Upgrade
object called upgrade for the transaction just created.
Queue a task to invoke the upgrade callback with upgrade and result as its arguments.
If the callback throws an exception, abort these steps with
UNKNOWN_ERR.
getObjectStore()
This method returns an
ObjectStoreSync
object to access the object store identified by the given name in the
database to which this
DatabaseSync object
is connected. The mode
passed to this method is used to create the
If an object store with the given name, compared in a case-sensitive manner,
does not already exist in the database, then this method will throw a newly constructed
DatabaseException
exception with code
NOT_FOUND_ERR.
getIndex()
This method returns an
IndexSync
object representing the named index on the given object store in this database.
If an index with the given name, compared in a case-sensitive manner,
does not already exist, then this method will throw a newly constructed
DatabaseException
exception with code
NOT_FOUND_ERR.
transaction()TransactionSync object.
In the following example, a database can be initially setup with an object store where the id property on stored objects is used as the key.
var dbReq = new DatabaseRequest();
var upgrade =
function(changes, db) {
changes.createObjectStore('Contact', 'id');
// after this - db.version === '1'
};
dbReq.open('AddressBook', '1', 'Address Book', upgrade);Later this database can be upgraded to add a secondary key for the contact name.
var upgrade =
function(changes, db) {
// db.version === '1'
changes.createIndex('ContactName', 'Contact', 'name', Index.MANY_TO_ONE);
// after this - db.version === '2'
};
var db = new DatabaseSync('AddressBook', '2', 'Address Book', upgrade);interface Upgrade {
void createObjectStore(in DOMString name,
in DOMString primaryKeyPath,
in optional boolean autoIncrement)
raises DatabaseException;
void createIndex( in DOMString name,
in DOMString storeName,
in DOMString keyPath,
in optional unsigned short cardinality)
raises DatabaseException;
void removeObjectStore(in DOMString storeName) raises DatabaseException;
void removeIndex( in DOMString indexName,
in DOMString storeName)
raises DatabaseException;
};
[Callback=FunctionOnly, NoInterfaceObject]
interface UpgradeCallback {
void upgrade(in Upgrade txn,
in Database db);
};createObjectStore()This method creates a new object store with the given name. The path of the primary key in objects stored in this object store is required to correctly create a unique identifier for each object. This path cannot be the empty string. If an optional sequence name is also provided, then the named sequence is used to populate a value in the object at the key path and that value is stored as the key value for the object.
If an object store with the same name, compared in a
case-sensitive manner, already exists, then this method will throw a
newly constructed DatabaseException
exception with code
CONSTRAINT_ERR.
If a sequence name is given for generating primary keys but a sequence with
that name does not exist, then this method will throw a
newly constructed DatabaseException
exception with code
NOT_FOUND_ERR.
createIndex()This method is used to define a new index on this object store. An index is created using four required parameters - name, object store name, key path, and cardinality - and two optional parameters - action to be performed when related object is deleted, and the name of the related object.
If an index with the same name, compared in a
case-sensitive manner, already exists, then this method will throw a
newly constructed DatabaseException
exception with code
CONSTRAINT_ERR.
If the object store with the given name does not exist, then this method will
throw a newly constructed
DatabaseException
exception with code
NOT_FOUND_ERR.
removeObjectStore()This method is used to destroy the object store as well as all associated indices.
If an object store with the given name, compared in a case-sensitive manner,
does not already exist, then this method will throw a newly constructed
DatabaseException
exception with code
NOT_FOUND_ERR.
removeIndex()This method is used to destroy the named index on the given object store.
If an index with the given name does not exist on the named object store,
then this method will throw a newly constructed
DatabaseException
exception with code
NOT_FOUND_ERR.
An object store is a persistent structure that holds key-value pairs sorted by keys so as to enable fast insertion and look up as well as ordered retrieval. The store provides a mechanism by which to read and write stored objects. Operations on an object store must follow the transaction used to create or obtain the object store.
Each ObjectStore
object provides access to a set of key/value pairs, which are sometimes
called items. Values can be any data type supported by the
structured clone algorithm [HTML5].
Keys themselves can be of any simple data type. They can be extracted
from objects being stored or generated from a monotonic sequence.
An object store does not allow multiple items to be stored with the same primary. key. If the same key is used to store a different item, then it will replace the object currently stored with the same key.
In the following example, we set up an object store to use the property id from objects to store as the key. This object store is designed to auto generate keys for each new object added to Contact.
var upgrade = function(changes, db) {
changes.createObjectStore('Contact', 'id', true);
});
var db = new DatabaseSync('AddressBook', '1', 'Address Book', upgrade);Using this database, we can store objects in the Contact object store.
var store = db.getObjectStore('Contact');
var lincoln = {name: 'Lincoln', number: '7012'};
var contact = store.put(lincoln);
// contact.id === 1A stored value can be retrieved using the same key used for storage.
var contact = store.get(1);
// contact.name === 'Lincoln'A second put operation will replace the object stored by the first put operation.
var abraham = {id: 1, name: 'Abraham', number: '2107'};
store.put(abraham);Now when the object store is read with the same key, the result is different compared to the object read earlier.
var contact = store.get(1);
// contact.id === 1 && contact.name === 'Abraham';Additionally, all the objects of an object store matching a certain range of secondary or primary keys can be retrieved in key order.
var range = new CursorRange(2, 4, CursorRange.INCLUDE_START & CursorRange.INCLUDE_END);
var count = store.forEach(function(key, cursor, value) {
// each value is a contact and each key is the id for that
// contact whose id is between 2 and 4, both inclusive
}, range);An object store arranges stored objects by their key, also referred to as the primary key. Each object contains the value of its primary key. Additionally, object stores may also have one or more secondary keys declared for stored objects. Indices store data about secondary keys and refer to a primary key somewhere in the object store.
interface ObjectStore {
const unsigned short READ_WRITE = 1;
const unsigned short READ_ONLY = 2;
const unsigned short DETACHED_READ = 6;
readonly attribute DOMString name;
readonly attribute DOMString keyPath;
readonly attribute DOMStringList indexNames;
readonly attribute unsigned short mode;
};
interface ObjectStoreSync : ObjectStore {
unsigned long long forEach(in CursorCallback callback,
in optional CursorRange range,
in optional unsigned short direction);
any put( in any obj) raises DatabaseException;
void delete( in any key) raises DatabaseException;
any get( in any key) raises DatabaseException;
};
On getting, name will
provide the name of the object store.
On getting, keyPath will
provide the path of the key used as the primary key of objects stored in this
object store. This path can be the empty string if the object value is used
as the key.
On getting, indexNames will
provide a list of the names of indexes on objects in this object store.
The put() method is used to
store the given object in the object store. Insert the object by performing the
insertion steps.
The get() method is used to retrieve
the object associated with the given key in the object store. The following steps
must be run for retrieving the object with the following parameters: key.
DatabaseException
exception with code
NOT_FOUND_ERR
and terminate these steps.
DatabaseException
exception with code
NOT_FOUND_ERR
and terminate these steps.
The delete() method is used to
delete the object associated with the given key in the object store. Delete the
object by performing the deletion steps.
Usually database objects are retrieved by means of the object's key. However, the key used for an object will not always contain the information required to provide rapid access to the data that may be needed.
An object can be retrieved using secondary keys, provided these keys
are defined for the object store holding such objects. If the upgrade()
function above were redefined as below, an index would be maintained
on the name property of objects in Contact.
var upgrade = function(changes, db) {
changes.createObjectStore('Contact', 'id', true);
changes.createIndex('ContactName', 'Contact', 'name', Index.MANY_TO_ONE);
});For example, the id of an object with name property value as 'Lincoln' can be retrieved using this ContactName index.
If, however, we wanted to retrieve the object with name property value as 'Lincoln' can be retrieved using this ContactName index.
Additionally, all the objects of an object store matching a certain range of secondary or primary keys can be retrieved in key order. When objects are retrieved from the Contact object store, they are arranged by the value of the id attribute. On the other hand, when objects are retrieved using the ContactName index, they are arranged by the value of the name attribute.
var range = new CursorRange('L', 'M');
var count = index.forEachObject(function(key, cursor, value) {
// each value is a contact and each key is the name for that
// contact whose name's first letter is either L or M
}, range);If, on the other hand, we only want to names but not the contact objects for a given range, then we can use a different mechanism for that.
var range = new CursorRange('L', 'M');
var count = index.forEach(function(key, cursor) {
// each key is the id of a contact
// whose name's first letter is either L or M
}, range);interface Index {
const unsigned short ONE_TO_ONE = 0;
const unsigned short MANY_TO_ONE = 1;
const unsigned short ONE_TO_MANY = 2;
const unsigned short MANY_TO_MANY = 3;
readonly attribute DOMString name;
readonly attribute DOMString keyPath;
readonly attribute unsigned short cardinality;
};
interface IndexSync: Index {
unsigned long long forEach( in CursorCallback callback,
in optional CursorRange range,
in optional unsigned short direction);
unsigned long long forEachObject(in CursorCallback callback,
in optional CursorRange range,
in optional unsigned short direction);
any get( in any key) raises DatabaseException;
any getObject( in any key) raises DatabaseException;
void delete( in any key) raises DatabaseException;
};The ONE_TO_ONE cardinality option indicates that the secondary key is unique to the object. If an object is stored with a secondary key that already exists in the object store.
The MANY_TO_ONE cardinality option indicates the secondary key may be used for multiple objects in the object store. That is, the key appears more than once, but for each stored object it can be used only once.
The ONE_TO_MANY cardinality option indicates that the secondary key might be used more than once for a given object. Secondary keys themselves are assumed to be unique, but multiple instances of the secondary key can be used per object.
The MANY_TO_MANY cardinality option indicates there can be multiple keys for any given object, and for any given key there can be many related objects.
On getting, name will
provide the name of the index.
On getting, keyPath will
provide the path of the key used as the secondary key of objects stored in this
index. This path can be the empty string if the object value is used
as the key.
On getting, cardinality will
describe whether duplicate objects can be found in the index for a given
secondary key. If the cardinality is
ONE_TO_ONE or
ONE_TO_MANY, duplicates will not occur. For the other
cardinality levels, duplicates may occur.
The get() method is used to
retrieve the object associated with the given secondary key in the object store
by looking up the primary keys for the given secondary key and performing the
retrieval steps for each of
the primary keys.
The delete() method is used to
delete the object associated with the given secondary key in the object store.
by looking up the primary keys for the given secondary key and performing the
deletion steps for each of the
primary keys.
Cursors are a mechanism by which applications iterate over the records in a database. Cursors can be used to get, put, and delete database records. If a database allows duplicate records, then cursors are the only mechanism by which to access anything other than the first duplicate for a given key. Although storage operations can be performed on a cursor, a cursor itself is a transient object with all the real storage happening in objects that yield the cursor.
The initial position of a cursor is set by specifying the direction in which the cursor should iterate over matching items. Once the cursor is created, it yield items only in that direction.
By default, a cursor walks over objects starting at the first item and ending at the last item including all the duplicates encountered along the way. If the cursor callback returns true, then the iteration is stopped.
var objects = ...
var cursor = objects.forEach(function(object) {;
// act on each object and return true to exit the cursor
});To start at the last item and end in the first item, the cursor should be created with the direction parameter.
To start at a certain key and end in the last item, i.e., for a lower-bounded cursor, while skipping duplicates, the cursor should be created with both the required start key and the direction parameter.
var objects = ...
var range = new CursorRange(CursorRange.INDIVIDUAL, key);
objects.forEach(function(object) {
// act on each object and return true to exit the cursor
}, range, Cursor.NEXT_NO_DUPLICATE);
It is also possible to create a bounded cursor, i.e., with
application-specified starting and ending points, the
cursor should be created with both the required keys.
If the range is inclusive of both keys, then additional
flags are required. In the following example, all keys
with values in the inclusive range (start,
end) are returned with all their duplicates,
from the beginning of the range to its end.
var objects = ...
var range = new CursorRange(CursorRange.INCLUDE_START & CursorRange.INCLUDE_END, start, end);
objects.forEach(function(object) {
// act on each object and return true to exit the cursor
}, range);interface Cursor {
const unsigned short NEXT = 0;
const unsigned short NEXT_NO_DUPLICATE = 1;
const unsigned short PREV = 2;
const unsigned short PREV_NO_DUPLICATE = 3;
readonly attribute unsigned short direction;
readonly attribute unsigned long long count;
void continue(in optional CursorRange skipTo);
boolean update( in any obj);
void delete();
};
[Constructor(in unsigned short mode, in optional any start, in optional end)]
interface CursorRange {
const unsigned short INCLUDE_START = 1;
const unsigned short INCLUDE_END = 2;
const unsigned short INDIVIDUAL = 4;
readonly attribute any start;
readonly attribute any end;
readonly attribute unsigned short mode;
};
[FunctionOnly, NoInterfaceObject]
interface CursorCallback {
bool handle(in any key,
in Cursor cursor,
in optional any value);
};
On getting count, the user agent
returns the total number of objects that share the current key.
On getting direction, the user agent
returns the the traversal direction of the cursor.
The forEach()
method is used to navigate items in a cursor in the direction
identified by the direction parameter, which can be
either of NEXT,
NEXT_NO_DUPLICATE,
PREV, or
PREV_NO_DUPLICATE.
If direction is unspecified, then the cursor uses
the NEXT direction.
The parameters inclusiveFrom and inclusiveTo
are used to signal whether ranges are open or closed on the
range boundaries.
For each object that matches the cursor condition, the user agent
calls the
CursorCallback. This callback is passed two
parameters - the object that matches the cursor condition, and
a Cursor object
to make changes to the database at the
Cursor position. If this callback returns a true
value, then the cursor traversal is exited whether or not the
range conditions have been satisfied.
The NEXT direction indicates
that cursor should yield all values, including duplicates
starting from the beginning of the range to its end.
The NEXT_NO_DUPLICATE direction
indicates that cursor should return all values, skipping over duplicate
objects for every key starting from the beginning of the range to
its end. For every key with duplicate values, only the first value
is yielded.
The PREV direction indicates
that cursor should yield all values, including duplicates
starting from the end of the range to its beginning.
The PREV_NO_DUPLICATE direction
indicates that cursor should return all values, skipping over duplicate
objects for every key starting from the end of the range to its
beginning. For every key with duplicate values, only the first value
is yielded.
The update() method is used to
update the object at which the cursor is currently positioned. If
the object is updated, then this method returns true. The update
method is only allowed if the
Cursor is obtained from an object store. The key of
the object parameter must match the key in the current
cursor position. Otherwise, the database will throw a newly constructed
DatabaseException
exception with code DATA_ERR.
The delete() method
is used to delete the object at which the cursor is currently
positioned. The delete()
method will return false if this method is called more than once
inside a CursorCallback.
A transaction represents an atomic and durable set of database access and mutation operations. Transactions offer data protection from application or system failures.
A transaction may be used to store multiple data items or to conditionally modify certain data items.
A Database object may
have at most one transaction at any given time.
Transactions are expected to be short lived. Conforming user agents may terminate transactions that take too long to complete in order to free up storage resources that are locked by a long running transaction.
interface TransactionSync {
void abort() raises DatabaseException;
void commit() raises DatabaseException;
};abort()Transaction object.
commit()Transaction object.
The API for a transaction consists of mechanisms for committing and rolling back the effects of database operations performed in that transaction. However, asynchronous databases also automatically commit a transaction whenever the transaction callback completes. Asynchronous databases also automatically rollback a transaction if an error occurs inside the transaction callback.
When the user agent is to create a
Transaction object
it must run the following steps:
NON_TRANSIENT_ERR
and jump to the last step.
TIMEOUT_ERR and
jump to the last step.
Transaction object
that represents that transaction. Let transaction be this object.
Database object
to transaction.
DatabaseException
exception exception with the code code.
Once a transaction is aborted or committed, that
Transaction
object can no longer be used. If any calls are made on that
object, then the database will throw a newly constructed
DatabaseException
exception with code NON_TRANSIENT_ERR.
To perform database operations under the control of a new transaction,
a fresh Transaction
object is needed.
The asynchronous database API returns without blocking the calling thread. This API can only be used in a Window or in a Worker [WebWorkers].
[NoInterfaceObject]
interface DBRequest {
void abort();
// states
const unsigned short INITIAL = 0;
const unsigned short LOADING = 1;
const unsigned short DONE = 2;
readonly attribute unsigned short readyState;
readonly attribute DatabaseError error;
//event handler attributes
attribute Function onsuccess;
attribute Function onerror;
};
DBRequest implements EventTarget;Events are fired during asynchronous database access as database objects are created and data is consumed from these objects. As requests for database objects are made, the user agent loads information about them into memory and when the required object handle is available, it alerts the application through the firing of events. The events are as follows:
| Event name | Interface | Dispatched when... |
|---|---|---|
success |
Event |
The database request has been completed and its results are available. |
error |
DatabaseErrorEvent |
There was an error performing the database request. |
commit |
Event |
The transaction commit has been completed. |
abort |
Event |
The transaction abort has been completed. |
This interface provides methods for opening databases and accessing database objects using event handler attributes [DOM3Events].
In the following example, we open a database asynchronously. Various event handlers are registered for responding to various situations.
var dbReq = new DatabaseRequest();
dbReq.onsuccess = function(evt) {...};
dbReq.onerror = function(evt) {...};
dbReq.open('AddressBook', '1', 'Address Book', function(txn, db) {...});This API uses an event handler mechanism to obtain database objects. If an error occurs while opening the database, an error handler is invoked. Otherwise, a success handler is invoked.
interface DatabaseRequest : DBRequest{
readonly attribute Database database;
void open(in DOMString name,
in DOMString version,
in DOMString displayName,
in optional UpgradeCallback upgrade,
in optional unsigned short isolation);
};
The open()
method takes the following arguments: a database name, a database
version, a display name, an optional upgrade callback, and an optional
isolation level. When called, this method must return immediately and
asynchronously perform the following steps, with all but the first two
and last steps being run atomically:
success,
with no namespace, which does not bubble, is not cancelable, and which uses the
DatabaseEvent interface
and result at each Window or WorkerUtils object.
The database failure steps performed with an error code are as follows:
error,
with no namespace, which does not bubble, is not cancelable, and which uses the
DatabaseErrorEvent interface
and error at each Window or WorkerUtils object.
The task source for these tasks is the database access task source.
In the following example, we open the same database as in the earlier
example using the asynchronous API. The storeReq is
initialized with an open() call.
var store = storeReq = null;
var dbReq = new DatabaseRequest();
dbReq.onsuccess =
function() {
storeReq = new ObjectStoreRequest(dbReq.database);
storeReq.open('Contact');
};
var upgrade =
function(upgrade, db) {
upgrade.createObjectStore('Contact', 'id');
});
dbReq.open('AddressBook', '1', 'Address Book');
An asynchronous call to put an object in the object store can be performed
once the open() call succeeds.
Objects in the object store can be read using their key.
The put() method is used to
store the given object in the object store. Insert the object by performing the
insertion steps.
The get() method is used to retrieve
the object associated with the given key in the object store. The following steps
must be run for retrieving the object with the following parameters: key.
The delete() method is used to
delete the object associated with the given key in the object store. Delete the
object by performing the deletion steps.
[Constructor(in Database db)]
interface CursorRequest : DBRequest {
readonly attribute unsigned long long count;
attribute CursorCallback callback;
void readStore( in DOMString storeName,
in optional CursorRange range,
in optional unsigned short direction);
void readIndex( in DOMString storeName,
in DOMString indexName,
in optional CursorRange range,
in optional unsigned short direction);
void readIndexObject(in DOMString storeName,
in DOMString indexName,
in optional CursorRange range,
in optional unsigned short direction);
};interface Transaction : TransactionSync {
// events
attribute Function oncommitted;
attribute Function onaborted;
};
[Constructor(in Database db)]
interface TransactionRequest : DBRequest {
readonly attribute Transaction transaction;
void open(in optional DOMStringList storeNames,
in optional unsigned int timeout);
};open()
When called, this
method must immediately return and then asynchronously perform the
transaction steps.
The transaction steps are as follows. These steps are invoked with a transaction callback, and the following optional arguments - a success callback, an error callback, an array of database objects for acquiring locks required in this transaction, and a timeout duration.
Transaction
object be transaction.
DatabaseError
with code
UNKNOWN_ERR.
The following are the event handlers (and their corresponding event handler event types) that must be supported, as IDL attributes, by objects implementing the various interfaces:
| Event handler | Supported by interfaces | Event handler event type |
|---|---|---|
onsuccess |
success |
DatabaseRequest
|
onerror |
error |
DatabaseRequest
|
oncommitted |
commit |
TransactionRequest |
onaborted |
abort |
TransactionRequest |
DatabaseException
exception with code CONSTRAINT_ERR
and terminate these steps.
Sequence object
for the sequence name
DatabaseException
exception with code DATA_ERR
and terminate these steps.
The retrieval steps are as follows. These steps must be run for retrieving the object with the following parameters: key.
DatabaseException
exception with code
NOT_FOUND_ERR and terminate these steps.
DatabaseException
exception with code
NOT_FOUND_ERR and
terminate these steps.
The deletion steps are as follows. These steps must be run with one parameter: key, key path, and optional sequence name.
DatabaseException
exception with code
NOT_FOUND_ERR and
terminate these steps.
Errors in the asynchronous database API are reported using
callbacks that have a
DatabaseError
object as one of their arguments.
interface DatabaseError {
const unsigned short UNKNOWN_ERR = 0;
const unsigned short NON_TRANSIENT_ERR = 1;
const unsigned short VERSION_ERR = 2;
const unsigned short NOT_FOUND_ERR = 3;
const unsigned short CONSTRAINT_ERR = 4;
const unsigned short DATA_ERR = 5;
const unsigned short FEATURE_ERR = 6;
const unsigned short SERIAL_ERR = 11;
const unsigned short TOO_LARGE_ERR = 12;
const unsigned short RECOVERABLE_ERR = 21;
const unsigned shrot TRANSIENT_ERR = 31;
const unsigned short TIMEOUT_ERR = 32;
unsigned short code;
int status;
DOMString message;
};The code IDL attribute must return the most appropriate code.
The status IDL attribute must return the detailed error status of the database.
The message IDL attribute must return an error message describing the error encountered. The message should be localized to the user's language.
exception DatabaseException {
const unsigned short UNKNOWN_ERR = 0;
const unsigned short NON_TRANSIENT_ERR = 1;
const unsigned short VERSION_ERR = 2;
const unsigned short NOT_FOUND_ERR = 3;
const unsigned short CONSTRAINT_ERR = 4;
const unsigned short DATA_ERR = 5;
const unsigned short FEATURE_ERR = 6;
const unsigned short SERIAL_ERR = 11;
const unsigned short TOO_LARGE_ERR = 12;
const unsigned short RECOVERABLE_ERR = 21;
const unsigned shrot TRANSIENT_ERR = 31;
const unsigned short TIMEOUT_ERR = 32;
unsigned short code;
int status;
DOMString message;
};The code IDL attribute must return the most appropriate code.
The status IDL attribute must return the detailed error status of the database.
The message IDL attribute must return an error message describing the exception raised. The message should be localized to the user's language.
The error codes and their meanings are given below.
| Constant | Situation |
|---|---|
UNKNOWN_ERR |
The operation failed for reasons unrelated to the database itself and not covered by any other error code. |
NON_TRANSIENT_ERR |
This error occurred because an operation was not allowed on an object. A retry of the same operation would fail unless the cause of the error is corrected. |
VERSION_ERR |
The operation failed because the actual database version was not what was expected or no upgrade function was passed even though the requested version was not found or the upgrade function threw an exception. |
NOT_FOUND_ERR |
The operation failed because the requested database object could not be found. For example, an object store did not exist but was being opened, or a sequence was used to create an object store, but did not exist. |
CONSTRAINT_ERR |
A mutation operation in the transaction failed due to a because a constraint was not satisfied. For example, an object such as an object store, sequence, or index already exists and a new one was being attempted to be created. |
DATA_ERR |
Data provided to an operation does not meet requirements, e.g., if the increment value for a sequence is set to 0. |
FEATURE_ERR |
A feature defined in this API was used but is not supported by the underlying implementation. |
SERIAL_ERR |
The operation failed because of the size of the data set being returned or because there was a problem in serializing or deserializing the object being processed. |
TOO_LARGE_ERR |
The operation failed because the data returned from the database was too large. |
RECOVERABLE_ERR |
The operation failed because the database was prevented from taking an action. The operation might be able to succeed if the application performs some recovery steps and retries the entire transaction. For example, there was not enough remaining storage space, or the storage quota was reached and the user declined to give more space to the database. |
TRANSIENT_ERR |
The operation failed because of some temporary problems. The failed operation might be able to succeed when the operation is retried without any intervention by application-level functionality. |
TIMEOUT_ERR |
A lock for the transaction could not be obtained in a reasonable time. |
DEADLOCK_ERR |
The current transaction was automatically rolled back by the database becuase of deadlock or other transaction serialization failures. |
A third-party host (or any object capable of getting content distributed to multiple sites) could use a unique identifier stored in its client-side database to track a user across multiple sessions, building a profile of the user's activities. In conjunction with a site that is aware of the user's real idobject (for example an e-commerce site that requires authenticated credentials), this could allow oppressive groups to target individuals with greater accuracy than in a world with purely anonymous Web usage.
There are a number of techniques that can be used to mitigate the risk of user tracking:
iframes.
User agents may automatically delete stored data after a period of time.
This can restrict the ability of a site to track a user, as the site would then only be able to track the user across multiple sessions when he authenticates with the site itself (e.g. by making a purchase or logging in to a service).
However, this also puts the user's data at risk.
User agents should present the database feature to the user in a way that associates them strongly with HTTP session cookies. [COOKIES]
This might encourage users to view such storage with healthy suspicion.
User agents may require the user to authorize access to databases before a site can use the feature.
User agents may record the origins of sites that contained content from third-party origins that caused data to be stored.
If this information is then used to present the view of data currently in persistent storage, it would allow the user to make informed decisions about which parts of the persistent storage to prune. Combined with a blacklist ("delete this data and prevent this domain from ever storing data again"), the user can restrict the use of persistent storage to sites that he trusts.
User agents may allow users to share their persistent storage domain blacklists.
This would allow communities to act together to protect their privacy.
While these suggestions prevent trivial use of this API for user tracking, they do not block it altogether. Within a single domain, a site can continue to track the user during a session, and can then pass all this information to the third party along with any identifying information (names, credit card numbers, addresses) obtained by the site. If a third party cooperates with multiple sites to obtain such information, a profile can still be created.
However, user tracking is to some extent possible even with no cooperation from the user agent whatsoever, for instance by using session identifiers in URLs, a technique already commonly used for innocuous purposes but easily repurposed for user tracking (even retroactively). This information can then be shared with other sites, using using visitors' IP addresses and other user-specific data (e.g. user-agent headers and configuration settings) to combine separate sessions into coherent user profiles.
User agents should treat persistently stored data as potentially sensitive; it's quite possible for e-mails, calendar appointments, health records, or other confidential documents to be stored in this mechanism.
To this end, user agents should ensure that when deleting data, it is promptly deleted from the underlying storage.