-
Notifications
You must be signed in to change notification settings - Fork 62
/
index.bs
6884 lines (5033 loc) · 260 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<pre class=metadata>
Title: Indexed Database API 3.0
Shortname: IndexedDB
Abstract: This document defines APIs for a database of records holding
simple values and hierarchical objects. Each record consists of a key
and some value. Moreover, the database maintains indexes over records
it stores. An application developer directly uses an API to locate
records either by their key or by using an index. A query language can
be layered on this API. An indexed database can be implemented using a
persistent B-tree data structure.
Status: ED
TR: https://www.w3.org/TR/IndexedDB/
Previous Version: from biblio indexeddb-3
ED: https://w3c.github.io/IndexedDB/
Level: 3
Editor: Ali Alabbas, Microsoft Corp. https://microsoft.com, alia@microsoft.com, w3cid 70376
Editor: Joshua Bell, Google Inc. https://google.com, jsbell@google.com, w3cid 61302
Group: webapps
Repository: w3c/IndexedDB
Test Suite: https://github.com/web-platform-tests/wpt/tree/master/IndexedDB
Favicon: logo-db.svg
Complain About: accidental-2119 yes
Markup Shorthands: css no, markdown yes
Include MDN Panels: yes
Assume Explicit For: yes
</pre>
<pre class=link-defaults>
spec:html; type:dfn; for:/; text:task queue
spec:webidl; type:interface; text:any
</pre>
<pre class=anchors>
spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
urlPrefix: dom.html
type: interface
text: Document; url: document
spec: ecma262; urlPrefix: https://tc39.github.io/ecma262/
type: dfn
url: sec-algorithm-conventions
text: !
text: ?
text: abrupt completion; url: sec-completion-record-specification-type
text: Array; url: sec-array-objects
text: array exotic object; url: array-exotic-objects
text: Array.prototype.sort; url: sec-array.prototype.sort
text: ArrayBuffer; url: sec-arraybuffer-objects
text: CreateDataProperty; url: sec-createdataproperty
text: current Realm; url: current-realm
text: Date; url: sec-date-objects
text: Get; url: sec-get-o-p
text: HasOwnProperty; url: sec-hasownproperty
text: IdentifierName; url: prod-IdentifierName
text: Number; url: sec-terms-and-definitions-number-type
text: Object; url: sec-object-objects
text: Realm; url: realm
text: Record; url: sec-list-and-record-specification-type
text: RegExp; url: sec-regexp-regular-expression-objects
text: ReturnIfAbrupt; url: sec-returnifabrupt
text: String; url: sec-terms-and-definitions-string-type
text: ToLength; url: sec-tolength
text: ToString; url: sec-tostring
text: Type; url: sec-ecmascript-data-types-and-values
text: TypeError; url: sec-native-error-types-used-in-this-standard-typeerror
text: Uint8Array; url: sec-typedarray-objects
spec: storage; urlPrefix: https://storage.spec.whatwg.org/
type: dfn
text: storage bucket; url: storage-bucket
text: storage key; url: storage-key
</pre>
<style>
/* Default ED/WD stylesheets set "both"; not needed for logo floated right */
div.head h1 { clear: left; }
table.props {
border-collapse: collapse;
border-style: hidden hidden none hidden;
}
table.props thead, table.props tbody {
border-bottom: solid;
}
table.props td, table.props th {
border-bottom: solid thin;
border-left: solid;
border-right: solid;
padding: 0.5em;
text-align: left;
vertical-align: top;
}
.domintro::before {
content: 'For web developers (non-normative)';
text-transform: initial;
}
.domintro dt {
font-family: Menlo, Consolas, "DejaVu Sans Mono", Monaco, monospace;
padding-top: 0.5em;
padding-bottom: 1em;
}
.domintro dt a {
color: inherit; border-bottom-style: none;
}
.domintro dt code {
font-size: inherit;
}
</style>
This is the Third Edition of Indexed Database API.
The [First Edition](https://www.w3.org/TR/2015/REC-IndexedDB-20150108/),
simply titled "Indexed Database API",
became a W3C Recommendation on 8 January 2015.
The [Second Edition](https://www.w3.org/TR/2018/REC-IndexedDB-2-20180130/),
titled "Indexed Database API 2.0",
became a W3C Recommendation on 30 January 2018.
Indexed Database API 3.0 is intended to supersede Indexed Database API 2.0.
<!-- ============================================================ -->
# Introduction # {#introduction}
<!-- ============================================================ -->
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 records.
<aside class=example id=example-open-connection>
The following example uses the API to access a `"library"` database. It has a `"books"` object store that holds books records stored by their `"isbn"` property as the primary key.
Book records have a `"title"` property. This example artificially requires that book titles are unique. The code enforces this by creating an index named `"by_title"` with the {{IDBIndexParameters/unique}} option set. This index is used to look up books by title, and will prevent adding books with non-unique titles.
Book records also have an `"author"` property, which is not <span class=allow-2119>required</span> to be unique. The code creates another index named `"by_author"` to allow look-ups by this property.
The code first opens a connection to the database. The <a event for=IDBOpenDBRequest>`upgradeneeded`</a> event handler code creates the object store and indexes, if needed. The <a event for=IDBRequest>`success`</a> event handler code saves the opened connection for use in later examples.
```js
const request = indexedDB.open("library");
let db;
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
const db = request.result;
const store = db.createObjectStore("books", {keyPath: "isbn"});
const titleIndex = store.createIndex("by_title", "title", {unique: true});
const authorIndex = store.createIndex("by_author", "author");
// Populate with initial data.
store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
};
request.onsuccess = function() {
db = request.result;
};
```
The following example populates the database using a transaction.
```js
const tx = db.transaction("books", "readwrite");
const store = tx.objectStore("books");
store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
tx.oncomplete = function() {
// All requests have succeeded and the transaction has committed.
};
```
The following example looks up a single book in the database by title
using an index.
```js
const tx = db.transaction("books", "readonly");
const store = tx.objectStore("books");
const index = store.index("by_title");
const request = index.get("Bedrock Nights");
request.onsuccess = function() {
const matching = request.result;
if (matching !== undefined) {
// A match was found.
report(matching.isbn, matching.title, matching.author);
} else {
// No match was found.
report(null);
}
};
```
The following example looks up all books in the database by author
using an index and a cursor.
```js
const tx = db.transaction("books", "readonly");
const store = tx.objectStore("books");
const index = store.index("by_author");
const request = index.openCursor(IDBKeyRange.only("Fred"));
request.onsuccess = function() {
const cursor = request.result;
if (cursor) {
// Called for each matching record.
report(cursor.value.isbn, cursor.value.title, cursor.value.author);
cursor.continue();
} else {
// No more matching records.
report(null);
}
};
```
The following example shows one way to handle errors when a request fails.
```js
const tx = db.transaction("books", "readwrite");
const store = tx.objectStore("books");
const request = store.put({title: "Water Buffaloes", author: "Slate", isbn: 987654});
request.onerror = function(event) {
// The uniqueness constraint of the "by_title" index failed.
report(request.error);
// Could call event.preventDefault() to prevent the transaction from aborting.
};
tx.onabort = function() {
// Otherwise the transaction will automatically abort due the failed request.
report(tx.error);
};
```
The database connection can be closed when it is no longer needed.
```js
db.close();
```
In the future, the database might have grown to contain other object stores and indexes. The following example shows one way to handle migrating from an older version.
```js
const request = indexedDB.open("library", 3); // Request version 3.
let db;
request.onupgradeneeded = function(event) {
const db = request.result;
if (event.oldVersion < 1) {
// Version 1 is the first version of the database.
const store = db.createObjectStore("books", {keyPath: "isbn"});
const titleIndex = store.createIndex("by_title", "title", {unique: true});
const authorIndex = store.createIndex("by_author", "author");
}
if (event.oldVersion < 2) {
// Version 2 introduces a new index of books by year.
const bookStore = request.transaction.objectStore("books");
const yearIndex = bookStore.createIndex("by_year", "year");
}
if (event.oldVersion < 3) {
// Version 3 introduces a new object store for magazines with two indexes.
const magazines = db.createObjectStore("magazines");
const publisherIndex = magazines.createIndex("by_publisher", "publisher");
const frequencyIndex = magazines.createIndex("by_frequency", "frequency");
}
};
request.onsuccess = function() {
db = request.result; // db.version will be 3.
};
```
</aside>
<aside class=example id=handling-versionchange>
A single database can be used by multiple clients (pages and workers)
simultaneously — transactions ensure they don't clash while reading and writing.
If a new client wants to upgrade the database (via the <a event for=IDBOpenDBRequest>`upgradeneeded`</a>
event), it cannot do so until all other clients close their connection to the
current version of the database.
To avoid blocking a new client from upgrading, clients can listen for the
<a event for=IDBDatabase>`versionchange`</a> event. This fires when another client is wanting to upgrade the
database. To allow this to continue, react to the <a event for=IDBDatabase>`versionchange`</a> event by doing
something that ultimately closes this client's [=/connection=] to the database.
One way of doing this is to reload the page:
```js
db.onversionchange = function() {
// First, save any unsaved data:
saveUnsavedData().then(function() {
// If the document isn't being actively used, it could be appropriate to reload
// the page without the user's interaction.
if (!document.hasFocus()) {
location.reload();
// Reloading will close the database, and also reload with the new JavaScript
// and database definitions.
} else {
// If the document has focus, it can be too disruptive to reload the page.
// Maybe ask the user to do it manually:
displayMessage("Please reload this page for the latest version.");
}
});
};
function saveUnsavedData() {
// How you do this depends on your app.
}
function displayMessage() {
// Show a non-modal message to the user.
}
```
Another way is to call the [=/connection=]'s {{IDBDatabase/close()}} method. However, you need to make
sure your app is aware of this, as subsequent attempts to access the database
will fail.
```js
db.onversionchange = function() {
saveUnsavedData().then(function() {
db.close();
stopUsingTheDatabase();
});
};
function stopUsingTheDatabase() {
// Put the app into a state where it no longer uses the database.
}
```
The new client (the one attempting the upgrade) can use the <a event for=IDBOpenDBRequest>`blocked`</a> event to
detect if other clients are preventing the upgrade from happening. The <a event for=IDBOpenDBRequest>`blocked`</a>
event fires if other clients still hold a connection to the database after their
<a event for=IDBDatabase>`versionchange`</a> events have fired.
```js
const request = indexedDB.open("library", 4); // Request version 4.
let blockedTimeout;
request.onblocked = function() {
// Give the other clients time to save data asynchronously.
blockedTimeout = setTimeout(function() {
displayMessage("Upgrade blocked - Please close other tabs displaying this site.");
}, 1000);
};
request.onupgradeneeded = function(event) {
clearTimeout(blockedTimeout);
hideMessage();
// ...
};
function hideMessage() {
// Hide a previously displayed message.
}
```
The user will only see the above message if another client fails to disconnect
from the database. Ideally the user will never see this.
</aside>
<!-- ============================================================ -->
# Constructs # {#constructs}
<!-- ============================================================ -->
A <dfn>name</dfn> is a [=string=] equivalent to a {{DOMString}};
that is, an arbitrary sequence of 16-bit code units of any length,
including the empty string. [=/Names=] are always compared as
opaque sequences of 16-bit code units.
<aside class=note>
As a result, [=/name=] comparison is sensitive to variations in case
as well as other minor variations such as normalization form, the
inclusion or omission of controls, and other variations in Unicode
text. [[Charmod-Norm]]
If an implementation uses a storage mechanism which does not support
arbitrary strings, the implementation can use an escaping mechanism
or something similar to map the provided name to a string that it
can store.
</aside>
<div algorithm>
To <dfn>create a sorted name list</dfn> from a [=/list=] |names|, run these steps:
1. Let |sorted| be |names| [=list/sorted in ascending order=] with the [=/code unit less than=] algorithm.
1. Return a new {{DOMStringList}} associated with |sorted|.
</div>
<details class=note>
<summary>Details</summary>
This matches the [=Array.prototype.sort=] on an [=Array=] of
[=Strings=]. This ordering compares the 16-bit code units in each
string, producing a highly efficient, consistent, and deterministic
sort order. The resulting list will not match any particular
alphabet or lexicographical order, particularly for code points
represented by a surrogate pair.
</details>
<!-- ============================================================ -->
## Database ## {#database-construct}
<!-- ============================================================ -->
Each [=/storage key=] has an associated set of [=/databases=]. A
<dfn>database</dfn> has zero or more [=/object stores=] which
hold the data stored in the database.
<div dfn-for=database>
A [=/database=] has a <dfn>name</dfn> which identifies it within a
specific [=/storage key=]. The name is a [=/name=],
and stays constant for the lifetime of the database.
A [=/database=] has a <dfn>version</dfn>. When a database is first
created, its [=database/version=] is 0 (zero).
<aside class=note>
Each [=/database=] has one version at a time; a [=/database=] can't
exist in multiple versions at once. The only way to change the
version is using an [=/upgrade transaction=].
</aside>
A [=/database=] has at most one associated <dfn>upgrade transaction</dfn>,
which is either null or an [=/upgrade transaction=], and is initially null.
</div>
<!-- ============================================================ -->
### Database connection ### {#database-connection}
<!-- ============================================================ -->
Script does not interact with [=/databases=] directly. Instead,
script has indirect access via a <dfn lt="connection|connected">connection</dfn>.
A [=/connection=] object can be used to manipulate the objects of
that [=/database=]. It is also the only way to obtain a
[=/transaction=] for that [=/database=].
The act of opening a [=/database=] creates a [=/connection=].
There may be multiple [=/connections=] to a given [=/database=] at
any given time.
A [=/connection=] can only access [=/databases=] associated with the
[=/storage key=] of the global scope from which the [=/connection=] is
opened.
<aside class=note>
This is not affected by changes to the {{Document}}'s
{{Document/domain}}.
</aside>
<div dfn-for=connection>
A [=/connection=] has a <dfn>version</dfn>, which is set when
the [=/connection=] is created. It remains constant for the
lifetime of the [=/connection=] unless an [=/abort
an upgrade transaction|upgrade is aborted=], in which
case it is set to the previous version of the [=/database=]. Once
the [=/connection=] is closed the
[=connection/version=] does not change.
Each connection has a <dfn>close pending flag</dfn> which is initially
false.
When a [=/connection=] is initially created it is in an opened
state. The connection can be <dfn>closed</dfn> through several means.
If the execution context where the [=/connection=] was created is
destroyed (for example due to the user navigating away from that
page), the connection is closed. The connection can also be closed
explicitly using the steps to [=close a database connection=]. When
the connection is closed its [=connection/close pending flag=] is always set to true if
it hasn't already been.
A [=/connection=] may be closed by a user agent in exceptional
circumstances, for example due to loss of access to the file system, a
permission change, or clearing of the [=/storage key=]'s storage. If this occurs
the user agent must run [=close a database
connection=] with the [=/connection=] and with the <var ignore>forced flag</var> set to true.
A [=/connection=] has an <dfn>object store set</dfn>, which is
initialized to the set of [=/object stores=] in the associated
[=/database=] when the [=/connection=] is created. The contents of the
set will remain constant except when an [=/upgrade transaction=] is
running.
A [=/connection=]'s [=get the parent=] algorithm returns
null.
An event with type <dfn event for=IDBDatabase>`versionchange`</dfn> will be fired at an open
[=/connection=] if an attempt is made to upgrade or delete the
[=/database=]. This gives the [=/connection=] the opportunity to close
to allow the upgrade or delete to proceed.
An event with type <dfn event for=IDBDatabase>`close`</dfn> will be fired at a [=/connection=] if the connection is [=/close a database connection|closed=] abnormally.
</div>
<!-- ============================================================ -->
## Object store ## {#object-store-construct}
<!-- ============================================================ -->
An <dfn>object store</dfn> is the primary storage mechanism for
storing data in a [=/database=].
<div dfn-for=object-store>
Each database has a set of [=/object stores=]. The set of [=/object
stores=] can be changed, but only using an [=/upgrade transaction=],
i.e. in response to an <a event for=IDBOpenDBRequest>`upgradeneeded`</a> event. When a
new database is created it doesn't contain any [=/object stores=].
An [=/object store=] has a <dfn>list of records</dfn> which hold the
data stored in the object store. Each <dfn>record</dfn> consists of a
[=/key=] and a [=/value=]. The list is sorted according to key in
[=ascending=] order. There can never be multiple records in a given object
store with the same key.
An [=/object store=] has a <dfn>name</dfn>, which is a [=/name=].
At any one time, the name is unique
within the [=/database=] to which it belongs.
An [=/object store=] optionally has a <dfn>key path</dfn>. If the
object store has a key path it is said to use <dfn>in-line keys</dfn>.
Otherwise it is said to use <dfn>out-of-line keys</dfn>.
An [=/object store=] optionally has a [=key generator=].
An object store can derive a [=/key=] for a [=object-store/record=] from
one of three sources:
1. A [=key generator=]. A key generator generates a monotonically
increasing numbers every time a key is needed.
1. Keys can be derived via a [=object-store/key path=].
1. Keys can also be explicitly specified when a [=/value=] is stored
in the object store.
</div>
<!-- ============================================================ -->
### Object store handle ### {#object-store-handle-construct}
<!-- ============================================================ -->
Script does not interact with [=/object stores=] directly. Instead,
within a [=/transaction=], script has indirect access via an
<dfn>object store handle</dfn>.
<div dfn-for=object-store-handle>
An [=/object store handle=] has an associated <dfn>object store</dfn>
and an associated <dfn>transaction</dfn>. Multiple handles may be
associated with the same [=/object store=] in different
[=/transactions=], but there must be only one [=/object store handle=]
associated with a particular [=/object store=] within a
[=/transaction=].
An [=/object store handle=] has an <dfn>index set</dfn>, which is
initialized to the set of [=/indexes=] that reference the associated
[=object-store-handle/object store=] when the [=/object store handle=]
is created. The contents of the set will remain constant except when
an [=/upgrade transaction=] is running.
An [=/object store handle=] has a <dfn>name</dfn>, which is
initialized to the [=object-store/name=] of the associated
[=object-store-handle/object store=] when the [=/object store handle=]
is created. The name will remain constant except when an [=/upgrade
transaction=] is running.
</div>
<!-- ============================================================ -->
## Values ## {#value-construct}
<!-- ============================================================ -->
Each record is associated with a <dfn>value</dfn>. User agents must
support any [=serializable object=]. This includes simple types
such as [=String=] primitive values and [=Date=] objects as well as
[=Object=] and [=Array=] instances, {{File}} objects, {{Blob}}
objects, {{ImageData}} objects, and so on. Record [=/values=] are
stored and retrieved by value rather than by reference; later changes
to a value have no effect on the record stored in the database.
Record [=/values=] are [=/Records=] output by the
[$StructuredSerializeForStorage$] operation.
<!-- ============================================================ -->
## Keys ## {#key-construct}
<!-- ============================================================ -->
In order to efficiently retrieve [=object-store/records=] stored in an indexed
database, each [=object-store/record=] is organized according to its
<dfn>key</dfn>.
<div dfn-for=key>
A [=/key=] has an associated <dfn>type</dfn> which is one of:
*number*,
*date*,
*string*,
*binary*,
or
*array*.
A [=/key=] also has an associated <dfn>value</dfn>, which will
be either:
an {{unrestricted double}} if type is *number* or *date*,
a {{DOMString}} if type is *string*,
a [=byte sequence=] if type is *binary*,
or a [=/list=] of other [=/keys=] if type is *array*.
</div>
An ECMAScript [[!ECMA-262]] value can be converted to a [=/key=] by
following the steps to [=convert a value to a key=].
<aside class=note>
The following ECMAScript types are valid keys:
* [=Number=] primitive values, except NaN. This includes Infinity
and -Infinity.
* [=Date=] objects, except where the \[[DateValue]]
internal slot is NaN.
* [=String=] primitive values.
* [=ArrayBuffer=] objects (or views on buffers such as
[=Uint8Array=]).
* [=Array=] objects, where every item is defined, is itself a valid
key, and does not directly or indirectly contain itself. This
includes empty arrays. Arrays can contain other arrays.
Attempting to convert other ECMAScript values to a [=/key=]
will fail.
</aside>
An <dfn>array key</dfn> is a [=/key=] with [=key/type=] *array*.
The <dfn>subkeys</dfn> of an [=array key=] are the [=list/items=] of the
[=array key=]'s [=key/value=].
<div algorithm>
To <dfn>compare two keys</dfn> |a| and |b|, run these steps:
1. Let |ta| be the [=key/type=] of |a|.
1. Let |tb| be the [=key/type=] of |b|.
1. If |ta| does not equal |tb|, then run these steps:
1. If |ta| is *array*, then return 1.
1. If |tb| is *array*, then return -1.
1. If |ta| is *binary*, then return 1.
1. If |tb| is *binary*, then return -1.
1. If |ta| is *string*, then return 1.
1. If |tb| is *string*, then return -1.
1. If |ta| is *date*, then return 1.
1. [=/Assert=]: |tb| is *date*.
1. Return -1.
1. Let |va| be the [=key/value=] of |a|.
1. Let |vb| be the [=key/value=] of |b|.
1. Switch on |ta|:
<dl class=switch>
: *number*
: *date*
::
1. If |va| is greater than |vb|, then return 1.
1. If |va| is less than |vb|, then return -1.
1. Return 0.
: *string*
::
1. If |va| is [=code unit less than=] |vb|, then return -1.
1. If |vb| is [=code unit less than=] |va|, then return 1.
1. Return 0.
: *binary*
::
1. If |va| is [=byte less than=] |vb|, then return -1.
1. If |vb| is [=byte less than=] |va|, then return 1.
1. Return 0.
: *array*
::
1. Let |length| be the lesser of |va|'s [=list/size=] and |vb|'s [=list/size=].
1. Let |i| be 0.
1. While |i| is less than |length|, then:
1. Let |c| be the result of recursively [=/comparing two keys=] with |va|[|i|] and |vb|[|i|].
1. If |c| is not 0, return |c|.
1. Increase |i| by 1.
1. If |va|'s [=list/size=] is greater than |vb|'s [=list/size=], then return 1.
1. If |va|'s [=list/size=] is less than |vb|'s [=list/size=], then return -1.
1. Return 0.
</dl>
</div>
The [=/key=] |a| is <dfn lt="greater than|ascending">greater than</dfn> the [=/key=] |b| if the
result of [=/comparing two keys=] with |a| and |b|
is 1.
The [=/key=] |a| is <dfn>less than</dfn> the [=/key=] |b| if the
result of [=/comparing two keys=] with |a| and |b|
is -1.
The [=/key=] |a| is <dfn>equal to</dfn> the [=/key=] |b| if the result
of [=/comparing two keys=] with |a| and |b| is 0.
<aside class=note>
As a result of the above rules, negative infinity is the lowest
possible value for a [=/key=].
*Number* keys are less than *date* keys.
*Date* keys are less than *string* keys.
*String* keys are less than *binary* keys.
*Binary* keys are less than *array* keys.
There is no highest possible [=/key=] value.
This is because an array of any candidate highest [=/key=]
followed by another [=/key=] is even higher.
</aside>
<aside class=note>
Members of *binary* keys are compared as unsigned [=byte=] values
(in the range 0 to 255 inclusive) rather than signed {{byte}} values (in the range
-128 to 127 inclusive).
</aside>
<!-- ============================================================ -->
## Key path ## {#key-path-construct}
<!-- ============================================================ -->
A <dfn>key path</dfn> is a string or list of strings
that defines how to extract a [=/key=]
from a [=/value=]. A <dfn>valid key path</dfn> is one of:
* An empty string.
* An <dfn>identifier</dfn>, which is a string matching the
[=IdentifierName=] production from the ECMAScript Language
Specification [[!ECMA-262]].
* A string consisting of two or more [=identifiers=] separated
by periods (U+002E FULL STOP).
* A non-empty list containing only strings
conforming to the above requirements.
<aside class=note>
Spaces are not allowed within a key path.
</aside>
[=/Key path=] values can only be accessed from properties explicitly
copied by [$StructuredSerializeForStorage$], as well as the
following type-specific properties:
<table class=props>
<tr><th>Type</th><th>Properties</th></tr>
<tr><td>{{Blob}}</td><td>{{Blob/size}}, {{Blob/type}}</td></tr>
<tr><td>{{File}}</td><td>{{File/name}}, {{File/lastModified}}</td></tr>
<tr><td>[=Array=]</td><td>`length`</td></tr>
<tr><td>[=String=]</td><td>`length`</td></tr>
</table>
<!-- ============================================================ -->
## Index ## {#index-construct}
<!-- ============================================================ -->
It is sometimes useful to retrieve [=object-store/records=] in an
[=/object store=] through other means than their
[=/key=]. An <dfn id=index-concept>index</dfn> allows looking up
[=object-store/records=] in an [=/object store=] using properties of the
[=/values=] in the [=/object stores=] [=object-store/records=].
<div dfn-for=index>
An index is a specialized persistent key-value storage and has a <dfn
lt="referenced|references">referenced</dfn> [=/object store=]. The
index has a <dfn>list of records</dfn> which hold the data stored in
the index. The <dfn>records</dfn> in an index are automatically populated
whenever records in the [=index/referenced=] object store are inserted,
updated or deleted. There can be several [=/indexes=] referencing the
same [=/object store=], in which changes to the object store cause all
such indexes to get updated.
The <dfn>values</dfn> in the index's [=index/records=] are always values of [=/keys=]
in the index's [=index/referenced=] object store. The <dfn>keys</dfn> are derived from
the referenced object store's [=/values=] using a <dfn>key path</dfn>.
If a given [=object-store/record=] with key |X| in the object store referenced by
the index has the value |A|, and [=/extract a key from a
value using a key path|evaluating=] the index's [=index/key path=]
on |A| yields the result |Y|, then the index will contain a record
with key |Y| and value |X|.
<aside class=example id=example-index-entries>
For example, if an index's [=index/referenced=] object store contains a
record with the key `123` and the value `{ name:
"Alice", title: "CEO" }`, and the index's [=index/key path=]
is "`name`" then the index would contain a record with
the key "`Alice`" and the value `123`.
</aside>
Records in an index are said to have a <dfn>referenced value</dfn>.
This is the value of the record in the index's referenced object store
which has a key equal to the index's record's value. So in the example
above, the record in the index whose [=index/key=] is |Y| and value is |X| has a
[=index/referenced value=] of |A|.
<aside class=example id=example-index-referenced-values>
In the preceding example, the record in the index with key
"`Alice`" and value `123` would have a
[=index/referenced value=] of `{ name: "Alice", title: "CEO"
}`.
</aside>
<aside class=note>
Each record in an index references one and only one record in the
index's [=index/referenced=] object store. However there can be multiple
records in an index which reference the same record in the object
store. And there can also be no records in an index which reference
a given record in an object store.
</aside>
The [=object-store/records=] in an index are always sorted according to the
[=object-store/record=]'s key. However unlike object stores, a given index can
contain multiple records with the same key. Such records are
additionally sorted according to the [=/index=]'s [=object-store/record=]'s value
(meaning the key of the record in the referenced [=/object store=]).
An [=/index=] has a <dfn>name</dfn>, which is a [=/name=].
At any one time, the name is
unique within index's [=index/referenced=] [=/object store=].
An [=/index=] has a <dfn>unique flag</dfn>. When
true, the index enforces that no two [=object-store/records=] in the index has
the same key. If a [=object-store/record=] in the index's referenced object
store is attempted to be inserted or modified such that evaluating the
index's key path on the records new value yields a result which
already exists in the index, then the attempted modification to the
object store fails.
An [=/index=] has a <dfn>multiEntry flag</dfn>. This flag affects how
the index behaves when the result of evaluating the index's
[=index/key path=] yields an [=array key=]. If its [=index/multiEntry flag=]
is false, then a single [=object-store/record=] whose [=/key=] is an [=array key=]
is added to the index. If its [=index/multiEntry flag=] is true, then
one [=object-store/record=] is added to the index for each of the [=subkeys=].
</div>
<!-- ============================================================ -->
### Index handle ### {#index-handle-construct}
<!-- ============================================================ -->
Script does not interact with [=/indexes=] directly. Instead, within
a [=/transaction=], script has indirect access via an <dfn>index
handle</dfn>.
<div dfn-for=index-handle>
An [=index handle=] has an associated <dfn>index</dfn> and an
associated <dfn>object store handle</dfn>. The <dfn>transaction</dfn>
of an [=index handle=] is the [=object-store-handle/transaction=] of
its associated [=/object store handle=]. Multiple handles may be
associated with the same [=/index=] in different [=/transactions=],
but there must be only one [=index handle=] associated with a
particular [=/index=] within a [=/transaction=].
An [=index handle=] has a <dfn>name</dfn>, which is initialized to the
[=index/name=] of the associated [=index-handle/index=] when the
[=index handle=] is created. The name will remain constant except when
an [=/upgrade transaction=] is running.
</div>
<!-- ============================================================ -->
## Transactions ## {#transaction-construct}
<!-- ============================================================ -->
A <dfn id=transaction-concept>transaction</dfn> is used to interact
with the data in a [=/database=]. Whenever data is read or written
to the database it is done by using a [=/transaction=].
<div dfn-for=transaction>
[=/Transactions=] offer some protection from application and system
failures. A [=/transaction=] may be used to store multiple data
records or to conditionally modify certain data records. A
[=/transaction=] represents an atomic and durable set of data access
and data mutation operations.
All transactions are created through a [=/connection=], which is the
transaction's <dfn>connection</dfn>.
A [=/transaction=] has a <dfn>scope</dfn> which is a [=/set=] of [=/object stores=] that the transaction may interact with.
Note: A [=/transaction=]'s [=transaction/scope=] remains fixed unless the [=/transaction=] is an [=/upgrade transaction=].
Two [=/transactions=] have <dfn lt="overlap|overlapping scope">overlapping scope</dfn> if any [=/object store=] is in both transactions' [=transaction/scope=].
A [=/transaction=] has a <dfn>mode</dfn> that determines which types
of interactions can be performed upon that transaction. The [=transaction/mode=]
is set when the transaction is created and remains fixed for the life
of the transaction. A [=/transaction=]'s [=transaction/mode=] is one of the
following:
: "{{IDBTransactionMode/readonly}}"
::
The transaction is only allowed to read data. No modifications can
be done by this type of transaction. This has the advantage that
several [=transaction/read-only transactions=] can run at the same time even
if their [=transaction/scopes=] are [=transaction/overlapping=], i.e. if they are using the
same object stores. This type of transaction can be created any
time once a database has been opened.
: "{{IDBTransactionMode/readwrite}}"
::
The transaction is allowed to read, modify and delete data from
existing object stores. However object stores and indexes can't be
added or removed. Multiple "{{IDBTransactionMode/readwrite}}" transactions
can't run at the same time if their [=transaction/scopes=] are [=transaction/overlapping=]
since that would mean that they can modify each other's data in
the middle of the transaction. This type of transaction can be
created any time once a database has been opened.
: "{{IDBTransactionMode/versionchange}}"
::
The transaction is allowed to read, modify and delete data from
existing object stores, and can also create and remove object
stores and indexes. It is the only type of transaction that can do
so. This type of transaction can't be manually created, but
instead is created automatically when an
<a event for=IDBOpenDBRequest>`upgradeneeded`</a> event is fired.
A [=/transaction=] has a <dfn>durability hint</dfn>. This is a hint to the user agent of whether to prioritize performance or durability when committing the transaction. The [=transaction/durability hint=] is one of the following:
: "{{IDBTransactionDurability/strict}}"
:: The user agent may consider that the [=/transaction=] has successfully [=transaction/committed=] only after verifying that all outstanding changes have been successfully written to a persistent storage medium.
: "{{IDBTransactionDurability/relaxed}}"
:: The user agent may consider that the [=/transaction=] has successfully [=transaction/committed=] as soon as all outstanding changes have been written to the operating system, without subsequent verification.
: "{{IDBTransactionDurability/default}}"
:: The user agent should use its default durability behavior for the [=/storage bucket=]. This is the default for [=/transactions=] if not otherwise specified.
<aside class=note>
In a typical implementation, "{{IDBTransactionDurability/strict}}" is a hint to the user agent to flush any operating system I/O buffers before a <a event for=IDBTransaction>`complete`</a> event is fired. While this provides greater confidence that the changes will be persisted in case of subsequent operating system crash or power loss, flushing buffers can take significant time and consume battery life on portable devices.
Web applications are encouraged to use "{{IDBTransactionDurability/relaxed}}" for ephemeral data such as caches or quickly changing records, and "{{IDBTransactionDurability/strict}}" in cases where reducing the risk of data loss outweighs the impact to performance and power. Implementations are encouraged to weigh the durability hint from applications against the impact to users and devices.
</aside>
A [=/transaction=] optionally has a <dfn>cleanup event loop</dfn>
which is an [=/event loop=].
A [=/transaction=] has a <dfn>request list</dfn> of pending [=/requests=]
which have been made against the transaction.
A [=/transaction=] has a <dfn>error</dfn> which is set if the
[=/transaction=] is [=transaction/aborted=].
A [=/transaction=]'s [=get the parent=] algorithm returns the
transaction's [=transaction/connection=].
A <dfn>read-only transaction</dfn> is
a [=/transaction=] with [=transaction/mode=] "{{IDBTransactionMode/readonly}}".
A <dfn>read/write transaction</dfn>
is a [=/transaction=] with [=transaction/mode=] "{{IDBTransactionMode/readwrite}}".
<!-- ============================================================ -->
### Transaction lifecycle ### {#transaction-lifecycle}
<!-- ============================================================ -->
A [=/transaction=] has a <dfn>state</dfn>, which is one
of the following:
: <dfn>active</dfn>
::
A transaction is in this state when it is first [=transaction/created=],
and during dispatch of an event from a [=/request=] associated with the transaction.
New [=/requests=] can be made against the transaction when it is in this state.