@@ -22,53 +22,48 @@ import { assert } from '../util/assert';
22
22
23
23
import { encode , EncodedResourcePath } from './encoded_resource_path' ;
24
24
25
- export const SCHEMA_VERSION = 1 ;
26
-
27
- /** Performs database creation and (in the future) upgrades between versions. */
28
- export function createOrUpgradeDb ( db : IDBDatabase , oldVersion : number ) : void {
29
- assert ( oldVersion === 0 , 'Unexpected upgrade from version ' + oldVersion ) ;
30
-
31
- db . createObjectStore ( DbMutationQueue . store , {
32
- keyPath : DbMutationQueue . keyPath
33
- } ) ;
34
-
35
- // TODO(mikelehen): Get rid of "as any" if/when TypeScript fixes their
36
- // types. https://github.com/Microsoft/TypeScript/issues/14322
37
- db . createObjectStore (
38
- DbMutationBatch . store ,
39
- // tslint:disable-next-line:no-any
40
- { keyPath : DbMutationBatch . keyPath as any }
41
- ) ;
25
+ /**
26
+ * Schema Version for the Web client (containing the Mutation Queue, the Query
27
+ * and the Remote Document Cache) and Multi-Tab Support.
28
+ */
29
+ export const SCHEMA_VERSION = 2 ;
42
30
43
- const targetDocumentsStore = db . createObjectStore (
44
- DbTargetDocument . store ,
45
- // tslint:disable-next-line:no-any
46
- { keyPath : DbTargetDocument . keyPath as any }
47
- ) ;
48
- targetDocumentsStore . createIndex (
49
- DbTargetDocument . documentTargetsIndex ,
50
- DbTargetDocument . documentTargetsKeyPath ,
51
- { unique : true }
31
+ /**
32
+ * Performs database creation and schema upgrades.
33
+ *
34
+ * Note that in production, this method is only ever used to upgrade the schema
35
+ * to SCHEMA_VERSION. Different versions are only used for testing and
36
+ * local feature development.
37
+ */
38
+ export function createOrUpgradeDb (
39
+ db : IDBDatabase ,
40
+ fromVersion : number ,
41
+ toVersion : number
42
+ ) : void {
43
+ // This function currently supports migrating to schema version 1 (Mutation
44
+ // Queue, Query and Remote Document Cache) and schema version 2 (Multi-Tab).
45
+ assert (
46
+ fromVersion < toVersion && fromVersion >= 0 && toVersion <= 2 ,
47
+ 'Unexpected schema upgrade from v${fromVersion} to v{toVersion}.'
52
48
) ;
53
49
54
- const targetStore = db . createObjectStore ( DbTarget . store , {
55
- keyPath : DbTarget . keyPath
56
- } ) ;
57
- // NOTE: This is unique only because the TargetId is the suffix.
58
- targetStore . createIndex (
59
- DbTarget . queryTargetsIndexName ,
60
- DbTarget . queryTargetsKeyPath ,
61
- { unique : true }
62
- ) ;
50
+ if ( fromVersion < 1 && toVersion >= 1 ) {
51
+ createOwnerStore ( db ) ;
52
+ createMutationQueue ( db ) ;
53
+ createQueryCache ( db ) ;
54
+ createRemoteDocumentCache ( db ) ;
55
+ }
63
56
64
- // NOTE: keys for these stores are specified explicitly rather than using a
65
- // keyPath.
66
- db . createObjectStore ( DbDocumentMutation . store ) ;
67
- db . createObjectStore ( DbRemoteDocument . store ) ;
68
- db . createObjectStore ( DbOwner . store ) ;
69
- db . createObjectStore ( DbTargetGlobal . store ) ;
57
+ if ( fromVersion < 2 && toVersion >= 2 ) {
58
+ createClientMetadataStore ( db ) ;
59
+ createTargetChangeStore ( db ) ;
60
+ }
70
61
}
71
62
63
+ // TODO(mikelehen): Get rid of "as any" if/when TypeScript fixes their types.
64
+ // https://github.com/Microsoft/TypeScript/issues/14322
65
+ type KeyPath = any ; // tslint:disable-line:no-any
66
+
72
67
/**
73
68
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
74
69
*/
@@ -94,6 +89,10 @@ export class DbOwner {
94
89
constructor ( public ownerId : string , public leaseTimestampMs : number ) { }
95
90
}
96
91
92
+ function createOwnerStore ( db : IDBDatabase ) : void {
93
+ db . createObjectStore ( DbOwner . store ) ;
94
+ }
95
+
97
96
/** Object keys in the 'mutationQueues' store are userId strings. */
98
97
export type DbMutationQueueKey = string ;
99
98
@@ -183,6 +182,18 @@ export class DbMutationBatch {
183
182
*/
184
183
export type DbDocumentMutationKey = [ string , EncodedResourcePath , BatchId ] ;
185
184
185
+ function createMutationQueue ( db : IDBDatabase ) : void {
186
+ db . createObjectStore ( DbMutationQueue . store , {
187
+ keyPath : DbMutationQueue . keyPath
188
+ } ) ;
189
+
190
+ db . createObjectStore ( DbMutationBatch . store , {
191
+ keyPath : DbMutationBatch . keyPath as KeyPath
192
+ } ) ;
193
+
194
+ db . createObjectStore ( DbDocumentMutation . store ) ;
195
+ }
196
+
186
197
/**
187
198
* An object to be stored in the 'documentMutations' store in IndexedDb.
188
199
*
@@ -241,6 +252,10 @@ export class DbDocumentMutation {
241
252
*/
242
253
export type DbRemoteDocumentKey = string [ ] ;
243
254
255
+ function createRemoteDocumentCache ( db : IDBDatabase ) : void {
256
+ db . createObjectStore ( DbRemoteDocument . store ) ;
257
+ }
258
+
244
259
/**
245
260
* Represents the known absence of a document at a particular version.
246
261
* Stored in IndexedDb as part of a DbRemoteDocument object.
@@ -455,11 +470,101 @@ export class DbTargetGlobal {
455
470
) { }
456
471
}
457
472
473
+ function createQueryCache ( db : IDBDatabase ) : void {
474
+ const targetDocumentsStore = db . createObjectStore ( DbTargetDocument . store , {
475
+ keyPath : DbTargetDocument . keyPath as KeyPath
476
+ } ) ;
477
+ targetDocumentsStore . createIndex (
478
+ DbTargetDocument . documentTargetsIndex ,
479
+ DbTargetDocument . documentTargetsKeyPath ,
480
+ { unique : true }
481
+ ) ;
482
+
483
+ const targetStore = db . createObjectStore ( DbTarget . store , {
484
+ keyPath : DbTarget . keyPath
485
+ } ) ;
486
+
487
+ // NOTE: This is unique only because the TargetId is the suffix.
488
+ targetStore . createIndex (
489
+ DbTarget . queryTargetsIndexName ,
490
+ DbTarget . queryTargetsKeyPath ,
491
+ { unique : true }
492
+ ) ;
493
+ db . createObjectStore ( DbTargetGlobal . store ) ;
494
+ }
495
+
496
+ /**
497
+ * An object representing the changes at a particular snapshot version for the
498
+ * given target. This is used to facilitate storing query changelogs in the
499
+ * targetChanges object store.
500
+ *
501
+ * PORTING NOTE: This is used for change propagation during multi-tab syncing
502
+ * and not needed on iOS and Android.
503
+ */
504
+ export class DbTargetChange {
505
+ /** Name of the IndexedDb object store. */
506
+ static store = 'targetChanges' ;
507
+
508
+ /** Keys are automatically assigned via the targetId and snapshotVersion. */
509
+ static keyPath = [ 'targetId' , 'snapshotVersion' ] ;
510
+
511
+ constructor (
512
+ /**
513
+ * The targetId identifying a target.
514
+ */
515
+ public targetId : TargetId ,
516
+ /**
517
+ * The snapshot version for this change.
518
+ */
519
+ public snapshotVersion : DbTimestamp ,
520
+ /**
521
+ * The keys of the changed documents in this snapshot.
522
+ */
523
+ public changes : {
524
+ added ?: EncodedResourcePath [ ] ;
525
+ modified ?: EncodedResourcePath [ ] ;
526
+ removed ?: EncodedResourcePath [ ] ;
527
+ }
528
+ ) { }
529
+ }
530
+
531
+ function createTargetChangeStore ( db : IDBDatabase ) : void {
532
+ db . createObjectStore ( DbTargetChange . store , {
533
+ keyPath : DbTargetChange . keyPath as KeyPath
534
+ } ) ;
535
+ }
536
+
458
537
/**
459
- * The list of all IndexedDB stored used by the SDK. This is used when creating
460
- * transactions so that access across all stores is done atomically.
538
+ * A record of the metadata state of each client.
539
+ *
540
+ * PORTING NOTE: This is used to synchronize multi-tab state and does not need
541
+ * to be ported to iOS or Android.
461
542
*/
462
- export const ALL_STORES = [
543
+ export class DbClientMetadata {
544
+ /** Name of the IndexedDb object store. */
545
+ static store = 'clientMetadata' ;
546
+
547
+ /** Keys are automatically assigned via the clientKey properties. */
548
+ static keyPath = [ 'clientKey' ] ;
549
+
550
+ constructor (
551
+ /** The auto-generated client key assigned at client startup. */
552
+ public clientKey : string ,
553
+ /** The last time this state was updated. */
554
+ public updateTimeMs : DbTimestamp ,
555
+ /** Whether this client is running in a foreground tab. */
556
+ public inForeground : boolean
557
+ ) { }
558
+ }
559
+
560
+ function createClientMetadataStore ( db : IDBDatabase ) : void {
561
+ db . createObjectStore ( DbClientMetadata . store , {
562
+ keyPath : DbClientMetadata . keyPath as KeyPath
563
+ } ) ;
564
+ }
565
+
566
+ // Visible for testing
567
+ export const V1_STORES = [
463
568
DbMutationQueue . store ,
464
569
DbMutationBatch . store ,
465
570
DbDocumentMutation . store ,
@@ -469,3 +574,12 @@ export const ALL_STORES = [
469
574
DbTargetGlobal . store ,
470
575
DbTargetDocument . store
471
576
] ;
577
+
578
+ const V2_STORES = [ DbClientMetadata . store , DbTargetChange . store ] ;
579
+
580
+ /**
581
+ * The list of all default IndexedDB stores used throughout the SDK. This is
582
+ * used when creating transactions so that access across all stores is done
583
+ * atomically.
584
+ */
585
+ export const ALL_STORES = [ ...V1_STORES , ...V2_STORES ] ;
0 commit comments