2016-01-05 01:10:31 +01:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
2016-01-05 14:20:13 +01:00
|
|
|
(C) 2016 GNUnet e.V.
|
2016-01-05 01:10:31 +01:00
|
|
|
|
|
|
|
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
|
2016-07-07 17:59:29 +02:00
|
|
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
2016-01-05 01:10:31 +01:00
|
|
|
*/
|
|
|
|
|
2016-01-05 14:20:13 +01:00
|
|
|
/**
|
|
|
|
* Database query abstractions.
|
|
|
|
* @module Query
|
|
|
|
* @author Florian Dold
|
|
|
|
*/
|
|
|
|
|
2019-11-21 23:09:43 +01:00
|
|
|
/**
|
|
|
|
* Imports.
|
|
|
|
*/
|
|
|
|
import { openPromise } from "./promiseUtils";
|
|
|
|
|
2017-05-28 01:10:54 +02:00
|
|
|
/**
|
|
|
|
* Result of an inner join.
|
|
|
|
*/
|
|
|
|
export interface JoinResult<L, R> {
|
2016-10-19 18:40:29 +02:00
|
|
|
left: L;
|
|
|
|
right: R;
|
|
|
|
}
|
|
|
|
|
2017-05-28 01:10:54 +02:00
|
|
|
/**
|
|
|
|
* Result of a left outer join.
|
|
|
|
*/
|
|
|
|
export interface JoinLeftResult<L, R> {
|
2016-11-16 01:59:39 +01:00
|
|
|
left: L;
|
|
|
|
right?: R;
|
|
|
|
}
|
|
|
|
|
2017-05-28 01:10:54 +02:00
|
|
|
/**
|
|
|
|
* Definition of an object store.
|
|
|
|
*/
|
2016-10-18 01:16:31 +02:00
|
|
|
export class Store<T> {
|
2019-08-01 23:21:15 +02:00
|
|
|
constructor(
|
|
|
|
public name: string,
|
|
|
|
public storeParams?: IDBObjectStoreParameters,
|
|
|
|
public validator?: (v: T) => T,
|
|
|
|
) {}
|
2016-10-18 01:16:31 +02:00
|
|
|
}
|
|
|
|
|
2017-11-30 04:07:36 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
function requestToPromise(req: IDBRequest): Promise<any> {
|
2019-12-03 00:52:15 +01:00
|
|
|
const stack = Error("Failed request was started here.");
|
2019-11-20 19:48:43 +01:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
req.onsuccess = () => {
|
|
|
|
resolve(req.result);
|
2017-11-30 04:07:36 +01:00
|
|
|
};
|
2019-11-20 19:48:43 +01:00
|
|
|
req.onerror = () => {
|
2019-11-21 23:09:43 +01:00
|
|
|
console.log("error in DB request", req.error);
|
2019-11-20 19:48:43 +01:00
|
|
|
reject(req.error);
|
2019-11-21 23:09:43 +01:00
|
|
|
console.log("Request failed:", stack);
|
2019-11-20 19:48:43 +01:00
|
|
|
};
|
|
|
|
});
|
2016-10-18 01:36:47 +02:00
|
|
|
}
|
|
|
|
|
2019-11-21 23:09:43 +01:00
|
|
|
function transactionToPromise(tx: IDBTransaction): Promise<void> {
|
|
|
|
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<T>(
|
2019-11-20 19:48:43 +01:00
|
|
|
db: IDBDatabase,
|
|
|
|
store: Store<T>,
|
|
|
|
key: any,
|
|
|
|
): Promise<T | undefined> {
|
|
|
|
const tx = db.transaction([store.name], "readonly");
|
|
|
|
const req = tx.objectStore(store.name).get(key);
|
2019-12-03 00:52:15 +01:00
|
|
|
const v = await requestToPromise(req);
|
2019-11-21 23:09:43 +01:00
|
|
|
await transactionToPromise(tx);
|
|
|
|
return v;
|
2016-01-11 02:56:32 +01:00
|
|
|
}
|
2016-01-05 14:20:13 +01:00
|
|
|
|
2019-11-21 23:09:43 +01:00
|
|
|
export async function oneShotGetIndexed<S extends IDBValidKey, T>(
|
2019-11-20 19:48:43 +01:00
|
|
|
db: IDBDatabase,
|
|
|
|
index: Index<S, T>,
|
|
|
|
key: any,
|
|
|
|
): Promise<T | undefined> {
|
|
|
|
const tx = db.transaction([index.storeName], "readonly");
|
2019-11-20 20:02:48 +01:00
|
|
|
const req = tx
|
|
|
|
.objectStore(index.storeName)
|
|
|
|
.index(index.indexName)
|
|
|
|
.get(key);
|
2019-11-21 23:09:43 +01:00
|
|
|
const v = await requestToPromise(req);
|
|
|
|
await transactionToPromise(tx);
|
|
|
|
return v;
|
2017-03-24 16:20:31 +01:00
|
|
|
}
|
|
|
|
|
2019-11-21 23:09:43 +01:00
|
|
|
export async function oneShotPut<T>(
|
2019-11-20 19:48:43 +01:00
|
|
|
db: IDBDatabase,
|
|
|
|
store: Store<T>,
|
|
|
|
value: T,
|
|
|
|
key?: any,
|
|
|
|
): Promise<any> {
|
|
|
|
const tx = db.transaction([store.name], "readwrite");
|
|
|
|
const req = tx.objectStore(store.name).put(value, key);
|
2019-11-21 23:09:43 +01:00
|
|
|
const v = await requestToPromise(req);
|
|
|
|
await transactionToPromise(tx);
|
|
|
|
return v;
|
2019-11-20 19:48:43 +01:00
|
|
|
}
|
2017-03-24 16:20:31 +01:00
|
|
|
|
2019-11-20 20:02:48 +01:00
|
|
|
function applyMutation<T>(
|
|
|
|
req: IDBRequest,
|
|
|
|
f: (x: T) => T | undefined,
|
|
|
|
): Promise<void> {
|
2019-11-20 19:48:43 +01:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
req.onsuccess = () => {
|
|
|
|
const cursor = req.result;
|
|
|
|
if (cursor) {
|
2019-11-21 23:09:43 +01:00
|
|
|
const val = cursor.value;
|
2019-11-20 19:48:43 +01:00
|
|
|
const modVal = f(val);
|
|
|
|
if (modVal !== undefined && modVal !== null) {
|
|
|
|
const req2: IDBRequest = cursor.update(modVal);
|
|
|
|
req2.onerror = () => {
|
|
|
|
reject(req2.error);
|
|
|
|
};
|
|
|
|
req2.onsuccess = () => {
|
|
|
|
cursor.continue();
|
|
|
|
};
|
2017-03-24 16:20:31 +01:00
|
|
|
} else {
|
2019-11-20 19:48:43 +01:00
|
|
|
cursor.continue();
|
2017-03-24 16:20:31 +01:00
|
|
|
}
|
|
|
|
} else {
|
2019-11-20 19:48:43 +01:00
|
|
|
resolve();
|
2017-03-24 16:20:31 +01:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
};
|
|
|
|
req.onerror = () => {
|
|
|
|
reject(req.error);
|
|
|
|
};
|
|
|
|
});
|
2017-03-24 16:20:31 +01:00
|
|
|
}
|
|
|
|
|
2019-11-21 23:09:43 +01:00
|
|
|
export async function oneShotMutate<T>(
|
2019-11-20 19:48:43 +01:00
|
|
|
db: IDBDatabase,
|
|
|
|
store: Store<T>,
|
|
|
|
key: any,
|
|
|
|
f: (x: T) => T | undefined,
|
|
|
|
): Promise<void> {
|
|
|
|
const tx = db.transaction([store.name], "readwrite");
|
|
|
|
const req = tx.objectStore(store.name).openCursor(key);
|
2019-11-21 23:09:43 +01:00
|
|
|
await applyMutation(req, f);
|
|
|
|
await transactionToPromise(tx);
|
2017-03-24 16:20:31 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
type CursorResult<T> = CursorEmptyResult<T> | CursorValueResult<T>;
|
2016-05-24 00:28:24 +02:00
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
interface CursorEmptyResult<T> {
|
|
|
|
hasValue: false;
|
2016-01-05 14:20:13 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
interface CursorValueResult<T> {
|
|
|
|
hasValue: true;
|
|
|
|
value: T;
|
2016-05-24 00:28:24 +02:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
class ResultStream<T> {
|
|
|
|
private currentPromise: Promise<void>;
|
|
|
|
private gotCursorEnd: boolean = false;
|
|
|
|
private awaitingResult: boolean = false;
|
|
|
|
|
|
|
|
constructor(private req: IDBRequest) {
|
|
|
|
this.awaitingResult = true;
|
|
|
|
let p = openPromise<void>();
|
|
|
|
this.currentPromise = p.promise;
|
|
|
|
req.onsuccess = () => {
|
|
|
|
if (!this.awaitingResult) {
|
|
|
|
throw Error("BUG: invariant violated");
|
2016-05-24 00:28:24 +02:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
const cursor = req.result;
|
|
|
|
if (cursor) {
|
|
|
|
this.awaitingResult = false;
|
|
|
|
p.resolve();
|
|
|
|
p = openPromise<void>();
|
|
|
|
this.currentPromise = p.promise;
|
|
|
|
} else {
|
|
|
|
this.gotCursorEnd = true;
|
|
|
|
p.resolve();
|
2016-01-05 14:20:13 +01:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
};
|
|
|
|
req.onerror = () => {
|
|
|
|
p.reject(req.error);
|
|
|
|
};
|
2016-10-19 23:55:58 +02:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
async toArray(): Promise<T[]> {
|
|
|
|
const arr: T[] = [];
|
|
|
|
while (true) {
|
|
|
|
const x = await this.next();
|
|
|
|
if (x.hasValue) {
|
|
|
|
arr.push(x.value);
|
|
|
|
} else {
|
|
|
|
break;
|
2016-10-19 23:55:58 +02:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
}
|
|
|
|
return arr;
|
2016-01-05 14:20:13 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
async map<R>(f: (x: T) => R): Promise<R[]> {
|
|
|
|
const arr: R[] = [];
|
|
|
|
while (true) {
|
|
|
|
const x = await this.next();
|
|
|
|
if (x.hasValue) {
|
|
|
|
arr.push(f(x.value));
|
|
|
|
} else {
|
|
|
|
break;
|
2016-01-05 14:20:13 +01:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
}
|
|
|
|
return arr;
|
2016-11-16 01:59:39 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
async forEach(f: (x: T) => void): Promise<void> {
|
|
|
|
while (true) {
|
|
|
|
const x = await this.next();
|
|
|
|
if (x.hasValue) {
|
|
|
|
f(x.value);
|
|
|
|
} else {
|
|
|
|
break;
|
2016-11-16 01:59:39 +01:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
}
|
2016-10-19 18:40:29 +02:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
async filter(f: (x: T) => boolean): Promise<T[]> {
|
|
|
|
const arr: T[] = [];
|
|
|
|
while (true) {
|
|
|
|
const x = await this.next();
|
|
|
|
if (x.hasValue) {
|
|
|
|
if (f(x.value)) {
|
2019-11-20 20:02:48 +01:00
|
|
|
arr.push(x.value);
|
2016-10-19 18:40:29 +02:00
|
|
|
}
|
2016-01-05 17:11:01 +01:00
|
|
|
} else {
|
2019-11-20 19:48:43 +01:00
|
|
|
break;
|
2016-01-05 14:20:13 +01:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
}
|
|
|
|
return arr;
|
2016-01-05 01:10:31 +01:00
|
|
|
}
|
2016-03-05 01:36:38 +01:00
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
async next(): Promise<CursorResult<T>> {
|
|
|
|
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 };
|
2016-03-05 01:36:38 +01:00
|
|
|
}
|
2016-01-05 01:10:31 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
export function oneShotIter<T>(
|
|
|
|
db: IDBDatabase,
|
2019-11-20 20:02:48 +01:00
|
|
|
store: Store<T>,
|
2019-11-20 19:48:43 +01:00
|
|
|
): ResultStream<T> {
|
|
|
|
const tx = db.transaction([store.name], "readonly");
|
|
|
|
const req = tx.objectStore(store.name).openCursor();
|
|
|
|
return new ResultStream<T>(req);
|
|
|
|
}
|
2017-03-24 16:49:45 +01:00
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
export function oneShotIterIndex<S extends IDBValidKey, T>(
|
|
|
|
db: IDBDatabase,
|
|
|
|
index: Index<S, T>,
|
|
|
|
query?: any,
|
|
|
|
): ResultStream<T> {
|
|
|
|
const tx = db.transaction([index.storeName], "readonly");
|
2019-11-20 20:02:48 +01:00
|
|
|
const req = tx
|
|
|
|
.objectStore(index.storeName)
|
|
|
|
.index(index.indexName)
|
|
|
|
.openCursor(query);
|
2019-11-20 19:48:43 +01:00
|
|
|
return new ResultStream<T>(req);
|
|
|
|
}
|
2017-08-27 05:42:46 +02:00
|
|
|
|
2019-12-05 19:38:19 +01:00
|
|
|
export class TransactionHandle {
|
2019-11-20 19:48:43 +01:00
|
|
|
constructor(private tx: IDBTransaction) {}
|
2016-01-05 01:10:31 +01:00
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
put<T>(store: Store<T>, value: T, key?: any): Promise<any> {
|
|
|
|
const req = this.tx.objectStore(store.name).put(value, key);
|
|
|
|
return requestToPromise(req);
|
2017-08-27 05:42:46 +02:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
add<T>(store: Store<T>, value: T, key?: any): Promise<any> {
|
|
|
|
const req = this.tx.objectStore(store.name).add(value, key);
|
|
|
|
return requestToPromise(req);
|
2016-10-18 01:36:47 +02:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
get<T>(store: Store<T>, key: any): Promise<T | undefined> {
|
|
|
|
const req = this.tx.objectStore(store.name).get(key);
|
|
|
|
return requestToPromise(req);
|
2016-11-18 04:09:04 +01:00
|
|
|
}
|
|
|
|
|
2019-12-03 00:52:15 +01:00
|
|
|
getIndexed<S extends IDBValidKey, T>(
|
|
|
|
index: Index<S, T>,
|
|
|
|
key: any,
|
|
|
|
): Promise<T | undefined> {
|
|
|
|
const req = this.tx
|
|
|
|
.objectStore(index.storeName)
|
|
|
|
.index(index.indexName)
|
|
|
|
.get(key);
|
|
|
|
return requestToPromise(req);
|
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
iter<T>(store: Store<T>, key?: any): ResultStream<T> {
|
|
|
|
const req = this.tx.objectStore(store.name).openCursor(key);
|
2019-11-20 20:02:48 +01:00
|
|
|
return new ResultStream<T>(req);
|
2016-01-05 14:20:13 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
delete<T>(store: Store<T>, key: any): Promise<void> {
|
|
|
|
const req = this.tx.objectStore(store.name).delete(key);
|
|
|
|
return requestToPromise(req);
|
2016-01-05 01:10:31 +01:00
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
mutate<T>(store: Store<T>, key: any, f: (x: T) => T | undefined) {
|
|
|
|
const req = this.tx.objectStore(store.name).openCursor(key);
|
|
|
|
return applyMutation(req, f);
|
2018-02-01 07:19:03 +01:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
}
|
2018-02-01 07:19:03 +01:00
|
|
|
|
2019-12-02 17:35:47 +01:00
|
|
|
export function runWithReadTransaction<T>(
|
|
|
|
db: IDBDatabase,
|
|
|
|
stores: Store<any>[],
|
|
|
|
f: (t: TransactionHandle) => Promise<T>,
|
|
|
|
): Promise<T> {
|
|
|
|
return runWithTransaction<T>(db, stores, f, "readonly");
|
|
|
|
}
|
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
export function runWithWriteTransaction<T>(
|
|
|
|
db: IDBDatabase,
|
|
|
|
stores: Store<any>[],
|
|
|
|
f: (t: TransactionHandle) => Promise<T>,
|
2019-12-02 17:35:47 +01:00
|
|
|
): Promise<T> {
|
|
|
|
return runWithTransaction<T>(db, stores, f, "readwrite");
|
|
|
|
}
|
|
|
|
|
|
|
|
function runWithTransaction<T>(
|
|
|
|
db: IDBDatabase,
|
|
|
|
stores: Store<any>[],
|
|
|
|
f: (t: TransactionHandle) => Promise<T>,
|
|
|
|
mode: "readonly" | "readwrite",
|
2019-11-20 19:48:43 +01:00
|
|
|
): Promise<T> {
|
2019-11-21 23:09:43 +01:00
|
|
|
const stack = Error("Failed transaction was started here.");
|
2019-11-20 19:48:43 +01:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const storeName = stores.map(x => x.name);
|
2019-12-02 17:35:47 +01:00
|
|
|
const tx = db.transaction(storeName, mode);
|
2019-11-20 19:48:43 +01:00
|
|
|
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));
|
2016-01-05 01:10:31 +01:00
|
|
|
}
|
2019-11-20 19:48:43 +01:00
|
|
|
resolve(funResult);
|
2016-01-11 02:56:32 +01:00
|
|
|
};
|
2019-11-21 23:09:43 +01:00
|
|
|
tx.onerror = () => {
|
|
|
|
console.error("error in transaction");
|
2019-12-05 19:38:19 +01:00
|
|
|
console.error(stack);
|
2019-11-21 23:09:43 +01:00
|
|
|
};
|
2019-11-20 19:48:43 +01:00
|
|
|
tx.onabort = () => {
|
2019-11-21 23:09:43 +01:00
|
|
|
if (tx.error) {
|
|
|
|
console.error("Transaction aborted with error:", tx.error);
|
|
|
|
} else {
|
|
|
|
console.log("Trasaction aborted (no error)");
|
|
|
|
}
|
|
|
|
reject(TransactionAbort);
|
2016-01-11 02:56:32 +01:00
|
|
|
};
|
2019-11-20 19:48:43 +01:00
|
|
|
const th = new TransactionHandle(tx);
|
|
|
|
const resP = f(th);
|
2019-12-03 00:52:15 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
2019-11-20 19:48:43 +01:00
|
|
|
});
|
|
|
|
}
|
2016-01-05 01:10:31 +01:00
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
/**
|
|
|
|
* Definition of an index.
|
|
|
|
*/
|
|
|
|
export class Index<S extends IDBValidKey, T> {
|
2017-12-12 21:54:14 +01:00
|
|
|
/**
|
2019-11-20 19:48:43 +01:00
|
|
|
* Name of the store that this index is associated with.
|
2017-12-12 21:54:14 +01:00
|
|
|
*/
|
2019-11-20 19:48:43 +01:00
|
|
|
storeName: string;
|
2017-12-12 21:54:14 +01:00
|
|
|
|
2016-02-23 14:07:53 +01:00
|
|
|
/**
|
2019-11-20 19:48:43 +01:00
|
|
|
* Options to use for the index.
|
2016-02-23 14:07:53 +01:00
|
|
|
*/
|
2019-11-20 19:48:43 +01:00
|
|
|
options: IndexOptions;
|
2016-02-23 14:07:53 +01:00
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
constructor(
|
|
|
|
s: Store<T>,
|
|
|
|
public indexName: string,
|
|
|
|
public keyPath: string | string[],
|
|
|
|
options?: IndexOptions,
|
|
|
|
) {
|
|
|
|
const defaultOptions = {
|
|
|
|
multiEntry: false,
|
2016-02-23 14:07:53 +01:00
|
|
|
};
|
2019-11-20 19:48:43 +01:00
|
|
|
this.options = { ...defaultOptions, ...(options || {}) };
|
|
|
|
this.storeName = s.name;
|
2016-01-05 01:10:31 +01:00
|
|
|
}
|
|
|
|
|
2016-01-11 02:56:32 +01:00
|
|
|
/**
|
2019-11-20 19:48:43 +01:00
|
|
|
* We want to have the key type parameter in use somewhere,
|
|
|
|
* because otherwise the compiler complains. In iterIndex the
|
|
|
|
* key type is pretty useful.
|
2016-01-11 02:56:32 +01:00
|
|
|
*/
|
2019-11-20 19:48:43 +01:00
|
|
|
protected _dummyKey: S | undefined;
|
|
|
|
}
|
2016-01-11 02:56:32 +01:00
|
|
|
|
2019-11-20 19:48:43 +01:00
|
|
|
/**
|
|
|
|
* Exception that should be thrown by client code to abort a transaction.
|
|
|
|
*/
|
2019-11-21 23:09:43 +01:00
|
|
|
export const TransactionAbort = Symbol("transaction_abort");
|