From e1369ff7e8fc02116b9c4261036f0e42e3423cf4 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 2 Dec 2019 00:42:40 +0100 Subject: the giant refactoring: split wallet into multiple parts --- src/query.ts | 446 ----------------------------------------------------------- 1 file changed, 446 deletions(-) delete mode 100644 src/query.ts (limited to 'src/query.ts') diff --git a/src/query.ts b/src/query.ts deleted file mode 100644 index 5726bcaa6..000000000 --- a/src/query.ts +++ /dev/null @@ -1,446 +0,0 @@ -/* - This file is part of TALER - (C) 2016 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ - -/** - * Database query abstractions. - * @module Query - * @author Florian Dold - */ - -/** - * Imports. - */ -import { openPromise } from "./promiseUtils"; - - -/** - * Result of an inner join. - */ -export interface JoinResult { - left: L; - right: R; -} - -/** - * Result of a left outer join. - */ -export interface JoinLeftResult { - left: L; - right?: R; -} - -/** - * Definition of an object store. - */ -export class Store { - constructor( - public name: string, - public storeParams?: IDBObjectStoreParameters, - public validator?: (v: T) => T, - ) {} -} - -/** - * Options for an index. - */ -export interface IndexOptions { - /** - * If true and the path resolves to an array, create an index entry for - * each member of the array (instead of one index entry containing the full array). - * - * Defaults to false. - */ - multiEntry?: boolean; -} - -function requestToPromise(req: IDBRequest): Promise { - const stack = Error("Failed request was started here.") - return new Promise((resolve, reject) => { - req.onsuccess = () => { - resolve(req.result); - }; - req.onerror = () => { - console.log("error in DB request", req.error); - reject(req.error); - console.log("Request failed:", stack); - }; - }); -} - -function transactionToPromise(tx: IDBTransaction): Promise { - const stack = Error("Failed transaction was started here."); - return new Promise((resolve, reject) => { - tx.onabort = () => { - reject(TransactionAbort); - }; - tx.oncomplete = () => { - resolve(); - }; - tx.onerror = () => { - console.error("Transaction failed:", stack); - reject(tx.error); - }; - }); -} - -export async function oneShotGet( - db: IDBDatabase, - store: Store, - key: any, -): Promise { - const tx = db.transaction([store.name], "readonly"); - const req = tx.objectStore(store.name).get(key); - const v = await requestToPromise(req) - await transactionToPromise(tx); - return v; -} - -export async function oneShotGetIndexed( - db: IDBDatabase, - index: Index, - key: any, -): Promise { - const tx = db.transaction([index.storeName], "readonly"); - const req = tx - .objectStore(index.storeName) - .index(index.indexName) - .get(key); - const v = await requestToPromise(req); - await transactionToPromise(tx); - return v; -} - -export async function oneShotPut( - db: IDBDatabase, - store: Store, - value: T, - key?: any, -): Promise { - const tx = db.transaction([store.name], "readwrite"); - const req = tx.objectStore(store.name).put(value, key); - const v = await requestToPromise(req); - await transactionToPromise(tx); - return v; -} - -function applyMutation( - req: IDBRequest, - f: (x: T) => T | undefined, -): Promise { - return new Promise((resolve, reject) => { - req.onsuccess = () => { - const cursor = req.result; - if (cursor) { - const val = cursor.value; - const modVal = f(val); - if (modVal !== undefined && modVal !== null) { - const req2: IDBRequest = cursor.update(modVal); - req2.onerror = () => { - reject(req2.error); - }; - req2.onsuccess = () => { - cursor.continue(); - }; - } else { - cursor.continue(); - } - } else { - resolve(); - } - }; - req.onerror = () => { - reject(req.error); - }; - }); -} - -export async function oneShotMutate( - db: IDBDatabase, - store: Store, - key: any, - f: (x: T) => T | undefined, -): Promise { - const tx = db.transaction([store.name], "readwrite"); - const req = tx.objectStore(store.name).openCursor(key); - await applyMutation(req, f); - await transactionToPromise(tx); -} - -type CursorResult = CursorEmptyResult | CursorValueResult; - -interface CursorEmptyResult { - hasValue: false; -} - -interface CursorValueResult { - hasValue: true; - value: T; -} - -class ResultStream { - private currentPromise: Promise; - private gotCursorEnd: boolean = false; - private awaitingResult: boolean = false; - - constructor(private req: IDBRequest) { - this.awaitingResult = true; - let p = openPromise(); - this.currentPromise = p.promise; - req.onsuccess = () => { - if (!this.awaitingResult) { - throw Error("BUG: invariant violated"); - } - const cursor = req.result; - if (cursor) { - this.awaitingResult = false; - p.resolve(); - p = openPromise(); - this.currentPromise = p.promise; - } else { - this.gotCursorEnd = true; - p.resolve(); - } - }; - req.onerror = () => { - p.reject(req.error); - }; - } - - async toArray(): Promise { - const arr: T[] = []; - while (true) { - const x = await this.next(); - if (x.hasValue) { - arr.push(x.value); - } else { - break; - } - } - return arr; - } - - async map(f: (x: T) => R): Promise { - const arr: R[] = []; - while (true) { - const x = await this.next(); - if (x.hasValue) { - arr.push(f(x.value)); - } else { - break; - } - } - return arr; - } - - async forEach(f: (x: T) => void): Promise { - while (true) { - const x = await this.next(); - if (x.hasValue) { - f(x.value); - } else { - break; - } - } - } - - async filter(f: (x: T) => boolean): Promise { - const arr: T[] = []; - while (true) { - const x = await this.next(); - if (x.hasValue) { - if (f(x.value)) { - arr.push(x.value); - } - } else { - break; - } - } - return arr; - } - - async next(): Promise> { - if (this.gotCursorEnd) { - return { hasValue: false }; - } - if (!this.awaitingResult) { - const cursor = this.req.result; - if (!cursor) { - throw Error("assertion failed"); - } - this.awaitingResult = true; - cursor.continue(); - } - await this.currentPromise; - if (this.gotCursorEnd) { - return { hasValue: false }; - } - const cursor = this.req.result; - if (!cursor) { - throw Error("assertion failed"); - } - return { hasValue: true, value: cursor.value }; - } -} - -export function oneShotIter( - db: IDBDatabase, - store: Store, -): ResultStream { - const tx = db.transaction([store.name], "readonly"); - const req = tx.objectStore(store.name).openCursor(); - return new ResultStream(req); -} - -export function oneShotIterIndex( - db: IDBDatabase, - index: Index, - query?: any, -): ResultStream { - const tx = db.transaction([index.storeName], "readonly"); - const req = tx - .objectStore(index.storeName) - .index(index.indexName) - .openCursor(query); - return new ResultStream(req); -} - -class TransactionHandle { - constructor(private tx: IDBTransaction) {} - - put(store: Store, value: T, key?: any): Promise { - const req = this.tx.objectStore(store.name).put(value, key); - return requestToPromise(req); - } - - add(store: Store, value: T, key?: any): Promise { - const req = this.tx.objectStore(store.name).add(value, key); - return requestToPromise(req); - } - - get(store: Store, key: any): Promise { - const req = this.tx.objectStore(store.name).get(key); - return requestToPromise(req); - } - - iter(store: Store, key?: any): ResultStream { - const req = this.tx.objectStore(store.name).openCursor(key); - return new ResultStream(req); - } - - delete(store: Store, key: any): Promise { - const req = this.tx.objectStore(store.name).delete(key); - return requestToPromise(req); - } - - mutate(store: Store, key: any, f: (x: T) => T | undefined) { - const req = this.tx.objectStore(store.name).openCursor(key); - return applyMutation(req, f); - } -} - -export function runWithWriteTransaction( - db: IDBDatabase, - stores: Store[], - f: (t: TransactionHandle) => Promise, -): Promise { - const stack = Error("Failed transaction was started here."); - return new Promise((resolve, reject) => { - const storeName = stores.map(x => x.name); - const tx = db.transaction(storeName, "readwrite"); - let funResult: any = undefined; - let gotFunResult: boolean = false; - tx.oncomplete = () => { - // This is a fatal error: The transaction completed *before* - // the transaction function returned. Likely, the transaction - // function waited on a promise that is *not* resolved in the - // microtask queue, thus triggering the auto-commit behavior. - // Unfortunately, the auto-commit behavior of IDB can't be switched - // of. There are some proposals to add this functionality in the future. - if (!gotFunResult) { - const msg = - "BUG: transaction closed before transaction function returned"; - console.error(msg); - reject(Error(msg)); - } - resolve(funResult); - }; - tx.onerror = () => { - console.error("error in transaction"); - }; - tx.onabort = () => { - if (tx.error) { - console.error("Transaction aborted with error:", tx.error); - } else { - console.log("Trasaction aborted (no error)"); - } - reject(TransactionAbort); - }; - const th = new TransactionHandle(tx); - const resP = f(th); - resP.then(result => { - gotFunResult = true; - funResult = result; - }).catch((e) => { - if (e == TransactionAbort) { - console.info("aborting transaction"); - } else { - tx.abort(); - console.error("Transaction failed:", e); - console.error(stack); - } - }); - }); -} - -/** - * Definition of an index. - */ -export class Index { - /** - * Name of the store that this index is associated with. - */ - storeName: string; - - /** - * Options to use for the index. - */ - options: IndexOptions; - - constructor( - s: Store, - public indexName: string, - public keyPath: string | string[], - options?: IndexOptions, - ) { - const defaultOptions = { - multiEntry: false, - }; - this.options = { ...defaultOptions, ...(options || {}) }; - this.storeName = s.name; - } - - /** - * We want to have the key type parameter in use somewhere, - * because otherwise the compiler complains. In iterIndex the - * key type is pretty useful. - */ - protected _dummyKey: S | undefined; -} - -/** - * Exception that should be thrown by client code to abort a transaction. - */ -export const TransactionAbort = Symbol("transaction_abort"); -- cgit v1.2.3