fix transaction issue and add safeguards

This commit is contained in:
Florian Dold 2017-03-24 16:49:45 +01:00
parent 970230da06
commit 76ef679688
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B

View File

@ -106,9 +106,9 @@ abstract class BaseQueryValue<T> implements QueryValue<T> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.subscribeOne((v, tx) => { this.subscribeOne((v, tx) => {
if (f(v)) { if (f(v)) {
onTrue(this.root); onTrue(new QueryRoot(this.root.db));
} else { } else {
onFalse(this.root); onFalse(new QueryRoot(this.root.db));
} }
}); });
resolve(); resolve();
@ -504,7 +504,7 @@ class IterQueryStream<T> extends QueryStreamBase<T> {
export class QueryRoot implements PromiseLike<void> { export class QueryRoot implements PromiseLike<void> {
private work: ((t: IDBTransaction) => void)[] = []; private work: ((t: IDBTransaction) => void)[] = [];
private db: IDBDatabase; db: IDBDatabase;
private stores = new Set(); private stores = new Set();
private kickoffPromise: Promise<void>; private kickoffPromise: Promise<void>;
@ -516,6 +516,8 @@ export class QueryRoot implements PromiseLike<void> {
private finishScheduled: boolean; private finishScheduled: boolean;
private finished: boolean = false;
constructor(db: IDBDatabase) { constructor(db: IDBDatabase) {
this.db = db; this.db = db;
} }
@ -524,13 +526,21 @@ export class QueryRoot implements PromiseLike<void> {
return this.finish().then(onfulfilled, onrejected); return this.finish().then(onfulfilled, onrejected);
} }
checkFinished() {
if (this.finished) {
throw Error("Can't add work to query after it was started");
}
}
iter<T>(store: Store<T>): QueryStream<T> { iter<T>(store: Store<T>): QueryStream<T> {
this.checkFinished();
this.stores.add(store.name); this.stores.add(store.name);
this.scheduleFinish(); this.scheduleFinish();
return new IterQueryStream(this, store.name, {}); return new IterQueryStream(this, store.name, {});
} }
count<T>(store: Store<T>): Promise<number> { count<T>(store: Store<T>): Promise<number> {
this.checkFinished();
const {resolve, promise} = openPromise(); const {resolve, promise} = openPromise();
const doCount = (tx: IDBTransaction) => { const doCount = (tx: IDBTransaction) => {
@ -549,6 +559,7 @@ export class QueryRoot implements PromiseLike<void> {
} }
deleteIf<T>(store: Store<T>, predicate: (x: T, n: number) => boolean): QueryRoot { deleteIf<T>(store: Store<T>, predicate: (x: T, n: number) => boolean): QueryRoot {
this.checkFinished();
const doDeleteIf = (tx: IDBTransaction) => { const doDeleteIf = (tx: IDBTransaction) => {
const s = tx.objectStore(store.name); const s = tx.objectStore(store.name);
const req = s.openCursor(); const req = s.openCursor();
@ -569,6 +580,7 @@ export class QueryRoot implements PromiseLike<void> {
iterIndex<S extends IDBValidKey,T>(index: Index<S,T>, iterIndex<S extends IDBValidKey,T>(index: Index<S,T>,
only?: S): QueryStream<T> { only?: S): QueryStream<T> {
this.checkFinished();
this.stores.add(index.storeName); this.stores.add(index.storeName);
this.scheduleFinish(); this.scheduleFinish();
return new IterQueryStream(this, index.storeName, { return new IterQueryStream(this, index.storeName, {
@ -583,6 +595,7 @@ export class QueryRoot implements PromiseLike<void> {
* in the store. * in the store.
*/ */
put<T>(store: Store<T>, val: T): QueryRoot { put<T>(store: Store<T>, val: T): QueryRoot {
this.checkFinished();
let doPut = (tx: IDBTransaction) => { let doPut = (tx: IDBTransaction) => {
tx.objectStore(store.name).put(val); tx.objectStore(store.name).put(val);
}; };
@ -593,6 +606,7 @@ export class QueryRoot implements PromiseLike<void> {
putWithResult<T>(store: Store<T>, val: T): Promise<IDBValidKey> { putWithResult<T>(store: Store<T>, val: T): Promise<IDBValidKey> {
this.checkFinished();
const {resolve, promise} = openPromise(); const {resolve, promise} = openPromise();
let doPutWithResult = (tx: IDBTransaction) => { let doPutWithResult = (tx: IDBTransaction) => {
let req = tx.objectStore(store.name).put(val); let req = tx.objectStore(store.name).put(val);
@ -609,6 +623,7 @@ export class QueryRoot implements PromiseLike<void> {
mutate<T>(store: Store<T>, key: any, f: (v: T) => T): QueryRoot { mutate<T>(store: Store<T>, key: any, f: (v: T) => T): QueryRoot {
this.checkFinished();
let doPut = (tx: IDBTransaction) => { let doPut = (tx: IDBTransaction) => {
let reqGet = tx.objectStore(store.name).get(key); let reqGet = tx.objectStore(store.name).get(key);
reqGet.onsuccess = () => { reqGet.onsuccess = () => {
@ -639,6 +654,7 @@ export class QueryRoot implements PromiseLike<void> {
* in the object store. * in the object store.
*/ */
putAll<T>(store: Store<T>, iterable: T[]): QueryRoot { putAll<T>(store: Store<T>, iterable: T[]): QueryRoot {
this.checkFinished();
const doPutAll = (tx: IDBTransaction) => { const doPutAll = (tx: IDBTransaction) => {
for (let obj of iterable) { for (let obj of iterable) {
tx.objectStore(store.name).put(obj); tx.objectStore(store.name).put(obj);
@ -655,6 +671,7 @@ export class QueryRoot implements PromiseLike<void> {
* in the object store. * in the object store.
*/ */
add<T>(store: Store<T>, val: T): QueryRoot { add<T>(store: Store<T>, val: T): QueryRoot {
this.checkFinished();
const doAdd = (tx: IDBTransaction) => { const doAdd = (tx: IDBTransaction) => {
tx.objectStore(store.name).add(val); tx.objectStore(store.name).add(val);
}; };
@ -667,6 +684,7 @@ export class QueryRoot implements PromiseLike<void> {
* Get one object from a store by its key. * Get one object from a store by its key.
*/ */
get<T>(store: Store<T>, key: any): Promise<T|undefined> { get<T>(store: Store<T>, key: any): Promise<T|undefined> {
this.checkFinished();
if (key === void 0) { if (key === void 0) {
throw Error("key must not be undefined"); throw Error("key must not be undefined");
} }
@ -691,6 +709,7 @@ export class QueryRoot implements PromiseLike<void> {
*/ */
getIndexed<I extends IDBValidKey,T>(index: Index<I,T>, getIndexed<I extends IDBValidKey,T>(index: Index<I,T>,
key: I): Promise<T|undefined> { key: I): Promise<T|undefined> {
this.checkFinished();
if (key === void 0) { if (key === void 0) {
throw Error("key must not be undefined"); throw Error("key must not be undefined");
} }
@ -727,6 +746,8 @@ export class QueryRoot implements PromiseLike<void> {
return this.kickoffPromise; return this.kickoffPromise;
} }
this.kickoffPromise = new Promise<void>((resolve, reject) => { this.kickoffPromise = new Promise<void>((resolve, reject) => {
// At this point, we can't add any more work
this.finished = true;
if (this.work.length == 0) { if (this.work.length == 0) {
resolve(); resolve();
return; return;
@ -750,6 +771,7 @@ export class QueryRoot implements PromiseLike<void> {
* Delete an object by from the given object store. * Delete an object by from the given object store.
*/ */
delete(storeName: string, key: any): QueryRoot { delete(storeName: string, key: any): QueryRoot {
this.checkFinished();
const doDelete = (tx: IDBTransaction) => { const doDelete = (tx: IDBTransaction) => {
tx.objectStore(storeName).delete(key); tx.objectStore(storeName).delete(key);
}; };