idb: don't create duplicate index entries

This commit is contained in:
Florian Dold 2019-08-26 02:41:50 +02:00
parent e2b7441e73
commit 6cea1f7c5b
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 281 additions and 822 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "idb-bridge", "name": "idb-bridge",
"version": "0.0.7", "version": "0.0.8",
"description": "IndexedDB implementation that uses SQLite3 as storage", "description": "IndexedDB implementation that uses SQLite3 as storage",
"main": "./build/index.js", "main": "./build/index.js",
"types": "./build/index.d.ts", "types": "./build/index.d.ts",
@ -12,7 +12,7 @@
"build": "tsc" "build": "tsc"
}, },
"devDependencies": { "devDependencies": {
"ava": "2.1.0", "ava": "2.3.0",
"typescript": "^3.4.5" "typescript": "^3.4.5"
} }
} }

View File

@ -261,8 +261,11 @@ export class MemoryBackend implements Backend {
throw Error("DB dump corrupt"); throw Error("DB dump corrupt");
} }
const objectStores: { [name: string]: ObjectStore } = {}; const objectStores: { [name: string]: ObjectStore } = {};
for (const objectStoreName of Object.keys(data.databases[dbName].objectStores)) { for (const objectStoreName of Object.keys(
const dumpedObjectStore = data.databases[dbName].objectStores[objectStoreName]; data.databases[dbName].objectStores,
)) {
const dumpedObjectStore =
data.databases[dbName].objectStores[objectStoreName];
const indexes: { [name: string]: Index } = {}; const indexes: { [name: string]: Index } = {};
for (const indexName of Object.keys(dumpedObjectStore.indexes)) { for (const indexName of Object.keys(dumpedObjectStore.indexes)) {
@ -270,21 +273,27 @@ export class MemoryBackend implements Backend {
const pairs = dumpedIndex.records.map((r: any) => { const pairs = dumpedIndex.records.map((r: any) => {
return structuredClone([r.indexKey, r]); return structuredClone([r.indexKey, r]);
}); });
const indexData: ISortedMapF<Key, IndexRecord> = new BTree(pairs, compareKeys); const indexData: ISortedMapF<Key, IndexRecord> = new BTree(
pairs,
compareKeys,
);
const index: Index = { const index: Index = {
deleted: false, deleted: false,
modifiedData: undefined, modifiedData: undefined,
modifiedName: undefined, modifiedName: undefined,
originalName: indexName, originalName: indexName,
originalData: indexData, originalData: indexData,
} };
indexes[indexName] = index; indexes[indexName] = index;
} }
const pairs = dumpedObjectStore.records.map((r: any) => { const pairs = dumpedObjectStore.records.map((r: any) => {
return structuredClone([r.primaryKey, r]); return structuredClone([r.primaryKey, r]);
}); });
const objectStoreData: ISortedMapF<Key, ObjectStoreRecord> = new BTree(pairs, compareKeys); const objectStoreData: ISortedMapF<Key, ObjectStoreRecord> = new BTree(
pairs,
compareKeys,
);
const objectStore: ObjectStore = { const objectStore: ObjectStore = {
deleted: false, deleted: false,
modifiedData: undefined, modifiedData: undefined,
@ -295,7 +304,7 @@ export class MemoryBackend implements Backend {
originalKeyGenerator: dumpedObjectStore.keyGenerator, originalKeyGenerator: dumpedObjectStore.keyGenerator,
committedIndexes: indexes, committedIndexes: indexes,
modifiedIndexes: {}, modifiedIndexes: {},
} };
objectStores[objectStoreName] = objectStore; objectStores[objectStoreName] = objectStore;
} }
const db: Database = { const db: Database = {
@ -310,13 +319,15 @@ export class MemoryBackend implements Backend {
} }
} }
private makeObjectStoreMap(database: Database): { [currentName: string]: ObjectStoreMapEntry } { private makeObjectStoreMap(
let map: { [currentName: string]: ObjectStoreMapEntry } = {} database: Database,
): { [currentName: string]: ObjectStoreMapEntry } {
let map: { [currentName: string]: ObjectStoreMapEntry } = {};
for (let objectStoreName in database.committedObjectStores) { for (let objectStoreName in database.committedObjectStores) {
const store = database.committedObjectStores[objectStoreName]; const store = database.committedObjectStores[objectStoreName];
const entry: ObjectStoreMapEntry = { const entry: ObjectStoreMapEntry = {
store, store,
indexMap: Object.assign({}, store.committedIndexes) indexMap: Object.assign({}, store.committedIndexes),
}; };
map[objectStoreName] = entry; map[objectStoreName] = entry;
} }
@ -581,7 +592,8 @@ export class MemoryBackend implements Backend {
if (!indexesSchema) { if (!indexesSchema) {
throw new Error("new index name already used"); throw new Error("new index name already used");
} }
const index: Index = myConn.objectStoreMap[objectStoreName].indexMap[oldName]; const index: Index =
myConn.objectStoreMap[objectStoreName].indexMap[oldName];
if (!index) { if (!index) {
throw Error("old index missing in connection's index map"); throw Error("old index missing in connection's index map");
} }
@ -592,7 +604,11 @@ export class MemoryBackend implements Backend {
index.modifiedName = newName; index.modifiedName = newName;
} }
deleteIndex(btx: DatabaseTransaction, objectStoreName: string, indexName: string): void { deleteIndex(
btx: DatabaseTransaction,
objectStoreName: string,
indexName: string,
): void {
if (this.enableTracing) { if (this.enableTracing) {
console.log(`TRACING: deleteIndex(${indexName})`); console.log(`TRACING: deleteIndex(${indexName})`);
} }
@ -614,7 +630,8 @@ export class MemoryBackend implements Backend {
if (!schema.objectStores[objectStoreName].indexes[indexName]) { if (!schema.objectStores[objectStoreName].indexes[indexName]) {
throw new Error("index does not exist"); throw new Error("index does not exist");
} }
const index: Index = myConn.objectStoreMap[objectStoreName].indexMap[indexName]; const index: Index =
myConn.objectStoreMap[objectStoreName].indexMap[indexName];
if (!index) { if (!index) {
throw Error("old index missing in connection's index map"); throw Error("old index missing in connection's index map");
} }
@ -782,7 +799,9 @@ export class MemoryBackend implements Backend {
originalName: indexName, originalName: indexName,
}; };
myConn.objectStoreMap[objectStoreName].indexMap[indexName] = newIndex; myConn.objectStoreMap[objectStoreName].indexMap[indexName] = newIndex;
db.modifiedObjectStores[objectStoreName].modifiedIndexes[indexName] = newIndex; db.modifiedObjectStores[objectStoreName].modifiedIndexes[
indexName
] = newIndex;
const schema = myConn.modifiedSchema; const schema = myConn.modifiedSchema;
if (!schema) { if (!schema) {
throw Error("no schema in versionchange tx"); throw Error("no schema in versionchange tx");
@ -798,7 +817,9 @@ export class MemoryBackend implements Backend {
throw Error("object store does not exist"); throw Error("object store does not exist");
} }
const storeData = objectStoreMapEntry.store.modifiedData || objectStoreMapEntry.store.originalData; const storeData =
objectStoreMapEntry.store.modifiedData ||
objectStoreMapEntry.store.originalData;
storeData.forEach((v, k) => { storeData.forEach((v, k) => {
this.insertIntoIndex(newIndex, k, v.value, indexProperties); this.insertIntoIndex(newIndex, k, v.value, indexProperties);
@ -837,7 +858,8 @@ export class MemoryBackend implements Backend {
const objectStoreMapEntry = myConn.objectStoreMap[objectStoreName]; const objectStoreMapEntry = myConn.objectStoreMap[objectStoreName];
if (!objectStoreMapEntry.store.modifiedData) { if (!objectStoreMapEntry.store.modifiedData) {
objectStoreMapEntry.store.modifiedData = objectStoreMapEntry.store.originalData; objectStoreMapEntry.store.modifiedData =
objectStoreMapEntry.store.originalData;
} }
let modifiedData = objectStoreMapEntry.store.modifiedData; let modifiedData = objectStoreMapEntry.store.modifiedData;
@ -877,13 +899,20 @@ export class MemoryBackend implements Backend {
throw Error("assertion failed"); throw Error("assertion failed");
} }
for (const indexName of Object.keys(schema.objectStores[objectStoreName].indexes)) { for (const indexName of Object.keys(
const index = myConn.objectStoreMap[objectStoreName].indexMap[indexName]; schema.objectStores[objectStoreName].indexes,
)) {
const index =
myConn.objectStoreMap[objectStoreName].indexMap[indexName];
if (!index) { if (!index) {
throw Error("index referenced by object store does not exist"); throw Error("index referenced by object store does not exist");
} }
this.enableTracing && console.log(`deleting from index ${indexName} for object store ${objectStoreName}`); this.enableTracing &&
const indexProperties = schema.objectStores[objectStoreName].indexes[indexName]; console.log(
`deleting from index ${indexName} for object store ${objectStoreName}`,
);
const indexProperties =
schema.objectStores[objectStoreName].indexes[indexName];
this.deleteFromIndex( this.deleteFromIndex(
index, index,
storeEntry.primaryKey, storeEntry.primaryKey,
@ -993,12 +1022,15 @@ export class MemoryBackend implements Backend {
const unique: boolean = const unique: boolean =
req.direction === "prevunique" || req.direction === "nextunique"; req.direction === "prevunique" || req.direction === "nextunique";
const storeData = objectStoreMapEntry.store.modifiedData || objectStoreMapEntry.store.originalData; const storeData =
objectStoreMapEntry.store.modifiedData ||
objectStoreMapEntry.store.originalData;
const haveIndex = req.indexName !== undefined; const haveIndex = req.indexName !== undefined;
if (haveIndex) { if (haveIndex) {
const index = myConn.objectStoreMap[req.objectStoreName].indexMap[req.indexName!]; const index =
myConn.objectStoreMap[req.objectStoreName].indexMap[req.indexName!];
const indexData = index.modifiedData || index.originalData; const indexData = index.modifiedData || index.originalData;
let indexPos = req.lastIndexPosition; let indexPos = req.lastIndexPosition;
@ -1275,7 +1307,8 @@ export class MemoryBackend implements Backend {
const objectStoreMapEntry = myConn.objectStoreMap[storeReq.objectStoreName]; const objectStoreMapEntry = myConn.objectStoreMap[storeReq.objectStoreName];
if (!objectStoreMapEntry.store.modifiedData) { if (!objectStoreMapEntry.store.modifiedData) {
objectStoreMapEntry.store.modifiedData = objectStoreMapEntry.store.originalData; objectStoreMapEntry.store.modifiedData =
objectStoreMapEntry.store.originalData;
} }
const modifiedData = objectStoreMapEntry.store.modifiedData; const modifiedData = objectStoreMapEntry.store.modifiedData;
@ -1296,13 +1329,15 @@ export class MemoryBackend implements Backend {
const storeKeyResult: StoreKeyResult = makeStoreKeyValue( const storeKeyResult: StoreKeyResult = makeStoreKeyValue(
storeReq.value, storeReq.value,
storeReq.key, storeReq.key,
objectStoreMapEntry.store.modifiedKeyGenerator || objectStoreMapEntry.store.originalKeyGenerator, objectStoreMapEntry.store.modifiedKeyGenerator ||
objectStoreMapEntry.store.originalKeyGenerator,
schema.objectStores[storeReq.objectStoreName].autoIncrement, schema.objectStores[storeReq.objectStoreName].autoIncrement,
schema.objectStores[storeReq.objectStoreName].keyPath, schema.objectStores[storeReq.objectStoreName].keyPath,
); );
key = storeKeyResult.key; key = storeKeyResult.key;
value = storeKeyResult.value; value = storeKeyResult.value;
objectStoreMapEntry.store.modifiedKeyGenerator = storeKeyResult.updatedKeyGenerator; objectStoreMapEntry.store.modifiedKeyGenerator =
storeKeyResult.updatedKeyGenerator;
const hasKey = modifiedData.has(key); const hasKey = modifiedData.has(key);
if (hasKey && storeReq.storeLevel !== StoreLevel.AllowOverwrite) { if (hasKey && storeReq.storeLevel !== StoreLevel.AllowOverwrite) {
@ -1315,15 +1350,22 @@ export class MemoryBackend implements Backend {
value: structuredClone(value), value: structuredClone(value),
}; };
objectStoreMapEntry.store.modifiedData = modifiedData.with(key, objectStoreRecord, true); objectStoreMapEntry.store.modifiedData = modifiedData.with(
key,
objectStoreRecord,
true,
);
for (const indexName of Object.keys(schema.objectStores[storeReq.objectStoreName] for (const indexName of Object.keys(
.indexes)) { schema.objectStores[storeReq.objectStoreName].indexes,
const index = myConn.objectStoreMap[storeReq.objectStoreName] .indexMap[indexName]; )) {
const index =
myConn.objectStoreMap[storeReq.objectStoreName].indexMap[indexName];
if (!index) { if (!index) {
throw Error("index referenced by object store does not exist"); throw Error("index referenced by object store does not exist");
} }
const indexProperties = schema.objectStores[storeReq.objectStoreName].indexes[indexName]; const indexProperties =
schema.objectStores[storeReq.objectStoreName].indexes[indexName];
this.insertIntoIndex(index, key, value, indexProperties); this.insertIntoIndex(index, key, value, indexProperties);
} }
@ -1353,13 +1395,16 @@ export class MemoryBackend implements Backend {
if (indexProperties.unique) { if (indexProperties.unique) {
throw new ConstraintError(); throw new ConstraintError();
} else { } else {
const newIndexRecord = { const pred = (x: Key) => compareKeys(x, primaryKey) === 0;
indexKey: indexKey, if (existingRecord.primaryKeys.findIndex(pred) === -1) {
primaryKeys: [primaryKey] const newIndexRecord = {
.concat(existingRecord.primaryKeys) indexKey: indexKey,
.sort(compareKeys), primaryKeys: [...existingRecord.primaryKeys, primaryKey].sort(
}; compareKeys,
index.modifiedData = indexData.with(indexKey, newIndexRecord, true); ),
};
index.modifiedData = indexData.with(indexKey, newIndexRecord, true);
}
} }
} else { } else {
const newIndexRecord: IndexRecord = { const newIndexRecord: IndexRecord = {
@ -1396,9 +1441,11 @@ export class MemoryBackend implements Backend {
objectStore.modifiedData = undefined; objectStore.modifiedData = undefined;
objectStore.modifiedName = undefined; objectStore.modifiedName = undefined;
objectStore.modifiedKeyGenerator = undefined; objectStore.modifiedKeyGenerator = undefined;
objectStore.modifiedIndexes = {} objectStore.modifiedIndexes = {};
for (const indexName in Object.keys(db.committedSchema.objectStores[objectStoreName].indexes)) { for (const indexName in Object.keys(
db.committedSchema.objectStores[objectStoreName].indexes,
)) {
const index = objectStore.committedIndexes[indexName]; const index = objectStore.committedIndexes[indexName];
index.deleted = false; index.deleted = false;
index.modifiedData = undefined; index.modifiedData = undefined;
@ -1451,7 +1498,6 @@ export class MemoryBackend implements Backend {
index.originalName = index.modifiedName || index.originalName; index.originalName = index.modifiedName || index.originalName;
store.committedIndexes[indexName] = index; store.committedIndexes[indexName] = index;
} }
} }
myConn.objectStoreMap = this.makeObjectStoreMap(db); myConn.objectStoreMap = this.makeObjectStoreMap(db);

File diff suppressed because it is too large Load Diff