@itwin/core-backend 5.15.0-dev.9 → 5.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -1
- package/lib/cjs/BlobContainerService.d.ts +2 -0
- package/lib/cjs/BlobContainerService.d.ts.map +1 -1
- package/lib/cjs/BlobContainerService.js.map +1 -1
- package/lib/cjs/BriefcaseManager.d.ts +2 -1
- package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
- package/lib/cjs/BriefcaseManager.js +39 -9
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/ChangeUnifier.d.ts +82 -0
- package/lib/cjs/ChangeUnifier.d.ts.map +1 -0
- package/lib/cjs/ChangeUnifier.js +163 -0
- package/lib/cjs/ChangeUnifier.js.map +1 -0
- package/lib/cjs/ChangesetReader.d.ts +21 -2
- package/lib/cjs/ChangesetReader.d.ts.map +1 -1
- package/lib/cjs/ChangesetReader.js +37 -1
- package/lib/cjs/ChangesetReader.js.map +1 -1
- package/lib/cjs/CloudSqlite.d.ts +15 -0
- package/lib/cjs/CloudSqlite.d.ts.map +1 -1
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/ECDb.d.ts +1 -0
- package/lib/cjs/ECDb.d.ts.map +1 -1
- package/lib/cjs/ECDb.js +4 -1
- package/lib/cjs/ECDb.js.map +1 -1
- package/lib/cjs/EditTxn.d.ts +61 -19
- package/lib/cjs/EditTxn.d.ts.map +1 -1
- package/lib/cjs/EditTxn.js +84 -29
- package/lib/cjs/EditTxn.js.map +1 -1
- package/lib/cjs/GeoCoordConfig.d.ts +1 -0
- package/lib/cjs/GeoCoordConfig.d.ts.map +1 -1
- package/lib/cjs/GeoCoordConfig.js +12 -8
- package/lib/cjs/GeoCoordConfig.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +22 -9
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +54 -12
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/NativeHost.d.ts.map +1 -1
- package/lib/cjs/NativeHost.js +5 -1
- package/lib/cjs/NativeHost.js.map +1 -1
- package/lib/cjs/Relationship.d.ts +28 -0
- package/lib/cjs/Relationship.d.ts.map +1 -1
- package/lib/cjs/Relationship.js +78 -0
- package/lib/cjs/Relationship.js.map +1 -1
- package/lib/cjs/SQLiteDb.d.ts +52 -5
- package/lib/cjs/SQLiteDb.d.ts.map +1 -1
- package/lib/cjs/SQLiteDb.js +40 -5
- package/lib/cjs/SQLiteDb.js.map +1 -1
- package/lib/cjs/SchemaSync.d.ts.map +1 -1
- package/lib/cjs/SchemaSync.js +1 -0
- package/lib/cjs/SchemaSync.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts +126 -89
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +114 -76
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/ViewStateHydrator.d.ts.map +1 -1
- package/lib/cjs/ViewStateHydrator.js.map +1 -1
- package/lib/cjs/ViewStore.d.ts.map +1 -1
- package/lib/cjs/ViewStore.js +62 -19
- package/lib/cjs/ViewStore.js.map +1 -1
- package/lib/cjs/annotations/LeaderGeometry.d.ts.map +1 -1
- package/lib/cjs/annotations/LeaderGeometry.js +3 -0
- package/lib/cjs/annotations/LeaderGeometry.js.map +1 -1
- package/lib/cjs/core-backend.d.ts +1 -0
- package/lib/cjs/core-backend.d.ts.map +1 -1
- package/lib/cjs/core-backend.js +1 -0
- package/lib/cjs/core-backend.js.map +1 -1
- package/lib/cjs/internal/BriefcaseUtils.d.ts +19 -0
- package/lib/cjs/internal/BriefcaseUtils.d.ts.map +1 -0
- package/lib/cjs/internal/BriefcaseUtils.js +65 -0
- package/lib/cjs/internal/BriefcaseUtils.js.map +1 -0
- package/lib/cjs/internal/QueryMetadataCache.d.ts +44 -0
- package/lib/cjs/internal/QueryMetadataCache.d.ts.map +1 -0
- package/lib/cjs/internal/QueryMetadataCache.js +107 -0
- package/lib/cjs/internal/QueryMetadataCache.js.map +1 -0
- package/lib/cjs/internal/Symbols.d.ts +9 -0
- package/lib/cjs/internal/Symbols.d.ts.map +1 -1
- package/lib/cjs/internal/Symbols.js +10 -1
- package/lib/cjs/internal/Symbols.js.map +1 -1
- package/lib/cjs/internal/workspace/WorkspaceImpl.d.ts.map +1 -1
- package/lib/cjs/internal/workspace/WorkspaceImpl.js +20 -6
- package/lib/cjs/internal/workspace/WorkspaceImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts +6 -6
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.js.map +1 -1
- package/lib/cjs/workspace/SettingsEditor.d.ts +4 -3
- package/lib/cjs/workspace/SettingsEditor.d.ts.map +1 -1
- package/lib/cjs/workspace/SettingsEditor.js +7 -5
- package/lib/cjs/workspace/SettingsEditor.js.map +1 -1
- package/lib/esm/BlobContainerService.d.ts +2 -0
- package/lib/esm/BlobContainerService.d.ts.map +1 -1
- package/lib/esm/BlobContainerService.js.map +1 -1
- package/lib/esm/BriefcaseManager.d.ts +2 -1
- package/lib/esm/BriefcaseManager.d.ts.map +1 -1
- package/lib/esm/BriefcaseManager.js +40 -10
- package/lib/esm/BriefcaseManager.js.map +1 -1
- package/lib/esm/ChangeUnifier.d.ts +82 -0
- package/lib/esm/ChangeUnifier.d.ts.map +1 -0
- package/lib/esm/ChangeUnifier.js +159 -0
- package/lib/esm/ChangeUnifier.js.map +1 -0
- package/lib/esm/ChangesetReader.d.ts +21 -2
- package/lib/esm/ChangesetReader.d.ts.map +1 -1
- package/lib/esm/ChangesetReader.js +38 -2
- package/lib/esm/ChangesetReader.js.map +1 -1
- package/lib/esm/CloudSqlite.d.ts +15 -0
- package/lib/esm/CloudSqlite.d.ts.map +1 -1
- package/lib/esm/CloudSqlite.js.map +1 -1
- package/lib/esm/ECDb.d.ts +1 -0
- package/lib/esm/ECDb.d.ts.map +1 -1
- package/lib/esm/ECDb.js +4 -1
- package/lib/esm/ECDb.js.map +1 -1
- package/lib/esm/EditTxn.d.ts +61 -19
- package/lib/esm/EditTxn.d.ts.map +1 -1
- package/lib/esm/EditTxn.js +85 -30
- package/lib/esm/EditTxn.js.map +1 -1
- package/lib/esm/GeoCoordConfig.d.ts +1 -0
- package/lib/esm/GeoCoordConfig.d.ts.map +1 -1
- package/lib/esm/GeoCoordConfig.js +12 -8
- package/lib/esm/GeoCoordConfig.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +22 -9
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +56 -14
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/NativeHost.d.ts.map +1 -1
- package/lib/esm/NativeHost.js +5 -1
- package/lib/esm/NativeHost.js.map +1 -1
- package/lib/esm/Relationship.d.ts +28 -0
- package/lib/esm/Relationship.d.ts.map +1 -1
- package/lib/esm/Relationship.js +78 -0
- package/lib/esm/Relationship.js.map +1 -1
- package/lib/esm/SQLiteDb.d.ts +52 -5
- package/lib/esm/SQLiteDb.d.ts.map +1 -1
- package/lib/esm/SQLiteDb.js +40 -5
- package/lib/esm/SQLiteDb.js.map +1 -1
- package/lib/esm/SchemaSync.d.ts.map +1 -1
- package/lib/esm/SchemaSync.js +1 -0
- package/lib/esm/SchemaSync.js.map +1 -1
- package/lib/esm/TxnManager.d.ts +126 -89
- package/lib/esm/TxnManager.d.ts.map +1 -1
- package/lib/esm/TxnManager.js +116 -78
- package/lib/esm/TxnManager.js.map +1 -1
- package/lib/esm/ViewStateHydrator.d.ts.map +1 -1
- package/lib/esm/ViewStateHydrator.js.map +1 -1
- package/lib/esm/ViewStore.d.ts.map +1 -1
- package/lib/esm/ViewStore.js +62 -19
- package/lib/esm/ViewStore.js.map +1 -1
- package/lib/esm/annotations/LeaderGeometry.d.ts.map +1 -1
- package/lib/esm/annotations/LeaderGeometry.js +3 -0
- package/lib/esm/annotations/LeaderGeometry.js.map +1 -1
- package/lib/esm/core-backend.d.ts +1 -0
- package/lib/esm/core-backend.d.ts.map +1 -1
- package/lib/esm/core-backend.js +1 -0
- package/lib/esm/core-backend.js.map +1 -1
- package/lib/esm/internal/BriefcaseUtils.d.ts +19 -0
- package/lib/esm/internal/BriefcaseUtils.d.ts.map +1 -0
- package/lib/esm/internal/BriefcaseUtils.js +61 -0
- package/lib/esm/internal/BriefcaseUtils.js.map +1 -0
- package/lib/esm/internal/QueryMetadataCache.d.ts +44 -0
- package/lib/esm/internal/QueryMetadataCache.d.ts.map +1 -0
- package/lib/esm/internal/QueryMetadataCache.js +103 -0
- package/lib/esm/internal/QueryMetadataCache.js.map +1 -0
- package/lib/esm/internal/Symbols.d.ts +9 -0
- package/lib/esm/internal/Symbols.d.ts.map +1 -1
- package/lib/esm/internal/Symbols.js +9 -0
- package/lib/esm/internal/Symbols.js.map +1 -1
- package/lib/esm/internal/workspace/WorkspaceImpl.d.ts.map +1 -1
- package/lib/esm/internal/workspace/WorkspaceImpl.js +20 -6
- package/lib/esm/internal/workspace/WorkspaceImpl.js.map +1 -1
- package/lib/esm/rpc-impl/IModelReadRpcImpl.d.ts +6 -6
- package/lib/esm/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
- package/lib/esm/rpc-impl/IModelReadRpcImpl.js.map +1 -1
- package/lib/esm/test/annotations/LeaderGeometry.test.js +15 -1
- package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js +144 -30
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
- package/lib/esm/test/ecdb/QueryMetadataCache.test.d.ts +2 -0
- package/lib/esm/test/ecdb/QueryMetadataCache.test.d.ts.map +1 -0
- package/lib/esm/test/ecdb/QueryMetadataCache.test.js +630 -0
- package/lib/esm/test/ecdb/QueryMetadataCache.test.js.map +1 -0
- package/lib/esm/test/element/DeleteDefinitionElements.test.js +403 -249
- package/lib/esm/test/element/DeleteDefinitionElements.test.js.map +1 -1
- package/lib/esm/test/element/RelationshipBatchLookup.test.d.ts +2 -0
- package/lib/esm/test/element/RelationshipBatchLookup.test.d.ts.map +1 -0
- package/lib/esm/test/element/RelationshipBatchLookup.test.js +202 -0
- package/lib/esm/test/element/RelationshipBatchLookup.test.js.map +1 -0
- package/lib/esm/test/hubaccess/BriefcaseManager.test.js +187 -1
- package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
- package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.d.ts +2 -0
- package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.d.ts.map +1 -0
- package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.js +142 -0
- package/lib/esm/test/hubaccess/SchemaImportOrphanColumns.test.js.map +1 -0
- package/lib/esm/test/imodel/IModelGeolocation.test.js +8 -6
- package/lib/esm/test/imodel/IModelGeolocation.test.js.map +1 -1
- package/lib/esm/test/imodel/IModelProps.test.d.ts +2 -0
- package/lib/esm/test/imodel/IModelProps.test.d.ts.map +1 -0
- package/lib/esm/test/imodel/IModelProps.test.js +411 -0
- package/lib/esm/test/imodel/IModelProps.test.js.map +1 -0
- package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.d.ts +2 -0
- package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.d.ts.map +1 -0
- package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.js +240 -0
- package/lib/esm/test/schema/SchemaViewPropertyOverrides.test.js.map +1 -0
- package/lib/esm/test/schema/SchemaViewValidation.test.js +2 -8
- package/lib/esm/test/schema/SchemaViewValidation.test.js.map +1 -1
- package/lib/esm/test/standalone/BriefcaseManagerDelete.test.d.ts +2 -0
- package/lib/esm/test/standalone/BriefcaseManagerDelete.test.d.ts.map +1 -0
- package/lib/esm/test/standalone/BriefcaseManagerDelete.test.js +159 -0
- package/lib/esm/test/standalone/BriefcaseManagerDelete.test.js.map +1 -0
- package/lib/esm/test/standalone/ChangeUnifier.test.d.ts +2 -0
- package/lib/esm/test/standalone/ChangeUnifier.test.d.ts.map +1 -0
- package/lib/esm/test/standalone/ChangeUnifier.test.js +732 -0
- package/lib/esm/test/standalone/ChangeUnifier.test.js.map +1 -0
- package/lib/esm/test/standalone/DeleteElements.test.js +962 -83
- package/lib/esm/test/standalone/DeleteElements.test.js.map +1 -1
- package/lib/esm/test/standalone/EditTxn.test.js +72 -1
- package/lib/esm/test/standalone/EditTxn.test.js.map +1 -1
- package/lib/esm/test/standalone/GeoCoordConfig.test.d.ts +2 -0
- package/lib/esm/test/standalone/GeoCoordConfig.test.d.ts.map +1 -0
- package/lib/esm/test/standalone/GeoCoordConfig.test.js +158 -0
- package/lib/esm/test/standalone/GeoCoordConfig.test.js.map +1 -0
- package/lib/esm/test/standalone/SQLiteDb.test.d.ts.map +1 -1
- package/lib/esm/test/standalone/SQLiteDb.test.js +422 -1
- package/lib/esm/test/standalone/SQLiteDb.test.js.map +1 -1
- package/lib/esm/test/standalone/ViewStoreDb.test.js +53 -0
- package/lib/esm/test/standalone/ViewStoreDb.test.js.map +1 -1
- package/lib/esm/test/workspace/SettingsContainers.test.d.ts +2 -0
- package/lib/esm/test/workspace/SettingsContainers.test.d.ts.map +1 -0
- package/lib/esm/test/workspace/SettingsContainers.test.js +95 -0
- package/lib/esm/test/workspace/SettingsContainers.test.js.map +1 -0
- package/lib/esm/workspace/SettingsEditor.d.ts +4 -3
- package/lib/esm/workspace/SettingsEditor.d.ts.map +1 -1
- package/lib/esm/workspace/SettingsEditor.js +7 -5
- package/lib/esm/workspace/SettingsEditor.js.map +1 -1
- package/package.json +18 -18
|
@@ -55,12 +55,14 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
55
55
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
56
56
|
});
|
|
57
57
|
import { expect } from "chai";
|
|
58
|
-
import
|
|
58
|
+
import * as sinon from "sinon";
|
|
59
|
+
import { BeDuration, DbConflictCause, DbConflictResolution, DbOpcode, DbResult, Logger, LogLevel, OpenMode } from "@itwin/core-bentley";
|
|
59
60
|
import { IModelJsFs } from "../../IModelJsFs";
|
|
60
61
|
import { IModelTestUtils } from "../IModelTestUtils";
|
|
61
62
|
import { SnapshotDb } from "../../IModelDb";
|
|
62
63
|
import { SQLiteDb } from "../../SQLiteDb";
|
|
63
64
|
import { SqliteChangesetReader } from "../../SqliteChangesetReader";
|
|
65
|
+
import { _nativeDb } from "../../internal/Symbols";
|
|
64
66
|
import "../TestUtils"; // registers the global mocha before/after hooks that start/stop the backend
|
|
65
67
|
describe("SQLiteDb", () => {
|
|
66
68
|
it("should create new SQLiteDb", async () => {
|
|
@@ -178,6 +180,8 @@ describe("SQLiteDb", () => {
|
|
|
178
180
|
expect(() => SqliteChangesetReader.openFile({ fileName: changesetFileName, db, disableSchemaCheck: true })).throws("db must be open");
|
|
179
181
|
});
|
|
180
182
|
describe("applyChangeset", () => {
|
|
183
|
+
const applyFlags = SQLiteDb.ApplyChangesetFlags;
|
|
184
|
+
const policy = SQLiteDb.ChangesetConflictPolicy;
|
|
181
185
|
const readRows = (db, tableName) => {
|
|
182
186
|
const rows = [];
|
|
183
187
|
db.withSqliteStatement(`SELECT id,val FROM ${tableName} ORDER BY id`, (stmt) => {
|
|
@@ -264,8 +268,425 @@ describe("SQLiteDb", () => {
|
|
|
264
268
|
// it is the caller's responsibility to abandon (or save) the partially-applied changes after catching the error
|
|
265
269
|
target.abandonChanges();
|
|
266
270
|
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base1" }, { id: 2, val: "fromTarget2" }]);
|
|
271
|
+
expect(() => target.applyChangeset(changesetFileName, applyFlags.None, policy.AbortOnAnyConflict)).throws();
|
|
272
|
+
target.abandonChanges();
|
|
273
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base1" }, { id: 2, val: "fromTarget2" }]);
|
|
267
274
|
target.closeDb();
|
|
268
275
|
});
|
|
276
|
+
const withChangeset = (name, sourceSql, targetSql, test) => {
|
|
277
|
+
const sourceFileName = IModelTestUtils.prepareOutputFile("SQLiteDb", `${name}-source.db`);
|
|
278
|
+
const targetFileName = IModelTestUtils.prepareOutputFile("SQLiteDb", `${name}-target.db`);
|
|
279
|
+
const changesetFileName = IModelTestUtils.prepareOutputFile("SQLiteDb", `${name}.changeset`);
|
|
280
|
+
const source = new SQLiteDb();
|
|
281
|
+
const target = new SQLiteDb();
|
|
282
|
+
try {
|
|
283
|
+
source.createDb(sourceFileName, undefined, { rawSQLite: true });
|
|
284
|
+
source.executeSQL("CREATE TABLE test1(id INTEGER PRIMARY KEY,val TEXT NOT NULL UNIQUE)");
|
|
285
|
+
source.executeSQL("INSERT INTO test1 VALUES (1,'base1'),(2,'base2')");
|
|
286
|
+
source.saveChanges();
|
|
287
|
+
source.closeDb();
|
|
288
|
+
IModelJsFs.copySync(sourceFileName, targetFileName);
|
|
289
|
+
source.openDb(sourceFileName, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
290
|
+
source.startChangeTracking();
|
|
291
|
+
for (const sql of sourceSql)
|
|
292
|
+
source.executeSQL(sql);
|
|
293
|
+
source.saveChanges();
|
|
294
|
+
source.createChangeset(changesetFileName);
|
|
295
|
+
target.openDb(targetFileName, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
296
|
+
for (const sql of targetSql)
|
|
297
|
+
target.executeSQL(sql);
|
|
298
|
+
target.saveChanges();
|
|
299
|
+
test(target, changesetFileName, targetFileName);
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
if (source.isOpen)
|
|
303
|
+
source.closeDb();
|
|
304
|
+
if (target.isOpen)
|
|
305
|
+
target.closeDb();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const conflictCases = [
|
|
309
|
+
{
|
|
310
|
+
name: "replace primary-key insert conflicts",
|
|
311
|
+
sourceSql: "INSERT INTO test1 VALUES (3,'incoming')",
|
|
312
|
+
targetSql: "INSERT INTO test1 VALUES (3,'existing')",
|
|
313
|
+
expected: [{ id: 1, val: "base1" }, { id: 2, val: "base2" }, { id: 3, val: "incoming" }],
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "skip deletes of missing rows",
|
|
317
|
+
sourceSql: "DELETE FROM test1 WHERE id=1",
|
|
318
|
+
targetSql: "DELETE FROM test1 WHERE id=1",
|
|
319
|
+
expected: [{ id: 2, val: "base2" }],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: "skip updates of missing rows",
|
|
323
|
+
sourceSql: "UPDATE test1 SET val='incoming' WHERE id=1",
|
|
324
|
+
targetSql: "DELETE FROM test1 WHERE id=1",
|
|
325
|
+
expected: [{ id: 2, val: "base2" }],
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: "replace conflicting update values",
|
|
329
|
+
sourceSql: "UPDATE test1 SET val='incoming' WHERE id=1",
|
|
330
|
+
targetSql: "UPDATE test1 SET val='existing' WHERE id=1",
|
|
331
|
+
expected: [{ id: 1, val: "incoming" }, { id: 2, val: "base2" }],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "apply deletes despite conflicting before-values",
|
|
335
|
+
sourceSql: "DELETE FROM test1 WHERE id=1",
|
|
336
|
+
targetSql: "UPDATE test1 SET val='existing' WHERE id=1",
|
|
337
|
+
expected: [{ id: 2, val: "base2" }],
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "skip incoming changes that violate unique constraints",
|
|
341
|
+
sourceSql: "INSERT INTO test1 VALUES (3,'incoming')",
|
|
342
|
+
targetSql: "UPDATE test1 SET val='incoming' WHERE id=2",
|
|
343
|
+
expected: [{ id: 1, val: "base1" }, { id: 2, val: "incoming" }],
|
|
344
|
+
},
|
|
345
|
+
];
|
|
346
|
+
for (const [index, testCase] of conflictCases.entries()) {
|
|
347
|
+
it(`should ${testCase.name} with the no-local-changes policy`, () => {
|
|
348
|
+
withChangeset(`no-local-changes-${index}`, [testCase.sourceSql], [testCase.targetSql], (target, changesetFileName, targetFileName) => {
|
|
349
|
+
const beforeApply = readRows(target, "test1");
|
|
350
|
+
expect(() => target.applyChangeset(changesetFileName)).throws();
|
|
351
|
+
target.abandonChanges();
|
|
352
|
+
expect(readRows(target, "test1")).deep.equal(beforeApply);
|
|
353
|
+
target.applyChangeset(changesetFileName, applyFlags.None, policy.NoLocalChanges);
|
|
354
|
+
expect(readRows(target, "test1")).deep.equal(testCase.expected);
|
|
355
|
+
target.abandonChanges();
|
|
356
|
+
expect(readRows(target, "test1")).deep.equal(beforeApply);
|
|
357
|
+
target.applyChangeset(changesetFileName, applyFlags.None, policy.NoLocalChanges);
|
|
358
|
+
target.saveChanges();
|
|
359
|
+
target.closeDb();
|
|
360
|
+
target.openDb(targetFileName, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
361
|
+
expect(readRows(target, "test1")).deep.equal(testCase.expected);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
it("should combine Invert and IgnoreNoop without invoking a conflict handler for no-op deletes", () => {
|
|
366
|
+
withChangeset("invert-ignore-noop", ["INSERT INTO test1 VALUES (3,'incoming')"], ["INSERT INTO test1 VALUES (3,'incoming')"], (target, file) => {
|
|
367
|
+
target.applyChangeset(file, applyFlags.Invert);
|
|
368
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base1" }, { id: 2, val: "base2" }]);
|
|
369
|
+
target.applyChangeset(file, applyFlags.Invert | applyFlags.IgnoreNoop, () => {
|
|
370
|
+
throw new Error("No-op delete should not invoke the conflict handler");
|
|
371
|
+
});
|
|
372
|
+
target.applyChangeset(file);
|
|
373
|
+
target.applyChangeset(file, applyFlags.IgnoreNoop, () => {
|
|
374
|
+
throw new Error("No-op insert should not invoke the conflict handler");
|
|
375
|
+
});
|
|
376
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base1" }, { id: 2, val: "base2" }, { id: 3, val: "incoming" }]);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
for (const insert of [false, true]) {
|
|
380
|
+
it(`should replace indirect ${insert ? "insert conflicts with a warning" : "update conflicts without warnings"} with the no-local-changes policy`, () => {
|
|
381
|
+
const indirectSql = insert ? "INSERT INTO test1 VALUES (3,'incoming')" : "UPDATE test1 SET val='incoming' WHERE id=2";
|
|
382
|
+
const targetSql = insert ? "INSERT INTO test1 VALUES (3,'existing')" : "UPDATE test1 SET val='existing' WHERE id=2";
|
|
383
|
+
withChangeset(`indirect-${insert ? "insert" : "update"}`, [
|
|
384
|
+
`CREATE TRIGGER indirect_change AFTER UPDATE ON test1 WHEN NEW.id=1 BEGIN ${indirectSql}; END`,
|
|
385
|
+
"UPDATE test1 SET val='direct' WHERE id=1",
|
|
386
|
+
], [targetSql], (target, file, targetFile) => {
|
|
387
|
+
const conflicts = [];
|
|
388
|
+
target.applyChangeset(file, applyFlags.None, (args) => {
|
|
389
|
+
conflicts.push(args);
|
|
390
|
+
return DbConflictResolution.Replace;
|
|
391
|
+
});
|
|
392
|
+
expect(conflicts).lengthOf(1);
|
|
393
|
+
expect(conflicts[0]).include({ cause: insert ? DbConflictCause.Conflict : DbConflictCause.Data, indirect: true });
|
|
394
|
+
target.abandonChanges();
|
|
395
|
+
const sandbox = sinon.createSandbox();
|
|
396
|
+
const category = "BeSQLite";
|
|
397
|
+
const levels = { ...Logger.categoryFilter, [category]: LogLevel.Warning };
|
|
398
|
+
sandbox.stub(Logger, "categoryFilter").get(() => levels);
|
|
399
|
+
const logWarning = sandbox.spy(Logger, "logWarning");
|
|
400
|
+
Logger.onLogLevelChanged.raiseEvent();
|
|
401
|
+
try {
|
|
402
|
+
target.applyChangeset(file, applyFlags.None, policy.NoLocalChanges);
|
|
403
|
+
expect(logWarning.calledWith(category)).equal(insert);
|
|
404
|
+
}
|
|
405
|
+
finally {
|
|
406
|
+
sandbox.restore();
|
|
407
|
+
Logger.onLogLevelChanged.raiseEvent();
|
|
408
|
+
}
|
|
409
|
+
const expected = insert
|
|
410
|
+
? [{ id: 1, val: "direct" }, { id: 2, val: "base2" }, { id: 3, val: "incoming" }]
|
|
411
|
+
: [{ id: 1, val: "direct" }, { id: 2, val: "incoming" }];
|
|
412
|
+
expect(readRows(target, "test1")).deep.equal(expected);
|
|
413
|
+
target.saveChanges();
|
|
414
|
+
target.closeDb();
|
|
415
|
+
target.openDb(targetFile, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
416
|
+
expect(readRows(target, "test1")).deep.equal(expected);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
it("should expose NoUpdateLoop independently of conflict policy", () => {
|
|
421
|
+
withChangeset("no-update-loop", [
|
|
422
|
+
"UPDATE test1 SET val='temp' WHERE id=1",
|
|
423
|
+
"UPDATE test1 SET val='base1' WHERE id=2",
|
|
424
|
+
"UPDATE test1 SET val='base2' WHERE id=1",
|
|
425
|
+
], [], (target, file) => {
|
|
426
|
+
target.applyChangeset(file);
|
|
427
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base2" }, { id: 2, val: "base1" }]);
|
|
428
|
+
target.abandonChanges();
|
|
429
|
+
expect(() => target.applyChangeset(file, applyFlags.NoUpdateLoop)).throws();
|
|
430
|
+
target.abandonChanges();
|
|
431
|
+
target.applyChangeset(file, applyFlags.NoUpdateLoop, policy.NoLocalChanges);
|
|
432
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base1" }, { id: 2, val: "base2" }]);
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
it("should let a custom handler skip a conflicting insert and retain its copied arguments", () => {
|
|
436
|
+
withChangeset("custom-conflict", ["INSERT INTO test1 VALUES (3,'incoming')"], ["INSERT INTO test1 VALUES (3,'existing')"], (target, file) => {
|
|
437
|
+
const conflicts = [];
|
|
438
|
+
target.applyChangeset(file, applyFlags.None, (args) => {
|
|
439
|
+
conflicts.push(args);
|
|
440
|
+
return DbConflictResolution.Skip;
|
|
441
|
+
});
|
|
442
|
+
expect(conflicts).deep.equal([{
|
|
443
|
+
cause: DbConflictCause.Conflict,
|
|
444
|
+
opcode: DbOpcode.Insert,
|
|
445
|
+
indirect: false,
|
|
446
|
+
tableName: "test1",
|
|
447
|
+
columnCount: 2,
|
|
448
|
+
primaryKeyColumns: [0],
|
|
449
|
+
}]);
|
|
450
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base1" }, { id: 2, val: "base2" }, { id: 3, val: "existing" }]);
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
it("should honor custom Replace and Abort resolutions and propagate callback exceptions", () => {
|
|
454
|
+
withChangeset("custom-resolution", ["UPDATE test1 SET val='incoming' WHERE id=1"], ["UPDATE test1 SET val='existing' WHERE id=1"], (target, file) => {
|
|
455
|
+
target.applyChangeset(file, applyFlags.None, (args) => {
|
|
456
|
+
expect(args.cause).equal(DbConflictCause.Data);
|
|
457
|
+
expect(args).deep.equal({
|
|
458
|
+
cause: DbConflictCause.Data,
|
|
459
|
+
opcode: DbOpcode.Update,
|
|
460
|
+
indirect: false,
|
|
461
|
+
tableName: "test1",
|
|
462
|
+
columnCount: 2,
|
|
463
|
+
primaryKeyColumns: [0],
|
|
464
|
+
});
|
|
465
|
+
return DbConflictResolution.Replace;
|
|
466
|
+
});
|
|
467
|
+
expect(readRows(target, "test1")[0]).deep.equal({ id: 1, val: "incoming" });
|
|
468
|
+
target.abandonChanges();
|
|
469
|
+
expect(() => target.applyChangeset(file, applyFlags.None, () => DbConflictResolution.Abort)).throws();
|
|
470
|
+
target.abandonChanges();
|
|
471
|
+
const error = new Error("custom conflict failure");
|
|
472
|
+
expect(() => target.applyChangeset(file, applyFlags.None, () => { throw error; })).throws(error);
|
|
473
|
+
target.abandonChanges();
|
|
474
|
+
expect(readRows(target, "test1")[0]).deep.equal({ id: 1, val: "existing" });
|
|
475
|
+
target.applyChangeset(file, applyFlags.None, () => DbConflictResolution.Replace);
|
|
476
|
+
expect(readRows(target, "test1")[0]).deep.equal({ id: 1, val: "incoming" });
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
it("should reject invalid flags, policies, and custom resolutions", () => {
|
|
480
|
+
withChangeset("invalid-apply-options", ["INSERT INTO test1 VALUES (3,'incoming')"], ["INSERT INTO test1 VALUES (3,'existing')"], (target, file) => {
|
|
481
|
+
const applyChangeset = target.applyChangeset.bind(target);
|
|
482
|
+
for (const flags of [-1, 16, 0.5, Number.NaN, Number.POSITIVE_INFINITY, true])
|
|
483
|
+
expect(() => Reflect.apply(applyChangeset, undefined, [file, flags])).throws(TypeError);
|
|
484
|
+
expect(() => Reflect.apply(applyChangeset, undefined, [file, applyFlags.None, 2])).throws(TypeError);
|
|
485
|
+
for (const resolution of [undefined, -1, 3, 0.5, Number.NaN, Promise.resolve(DbConflictResolution.Skip)]) {
|
|
486
|
+
expect(() => Reflect.apply(applyChangeset, undefined, [file, applyFlags.None, () => resolution])).throws(TypeError);
|
|
487
|
+
target.abandonChanges();
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
it("should reject reentrant lifecycle and transaction changes while allowing conflict queries", () => {
|
|
492
|
+
withChangeset("reentrant-apply", ["UPDATE test1 SET val='incoming' WHERE id=1"], ["UPDATE test1 SET val='existing' WHERE id=1"], (target, file, targetFile) => {
|
|
493
|
+
const nativeDb = target[_nativeDb];
|
|
494
|
+
const operations = [
|
|
495
|
+
() => target.saveChanges(),
|
|
496
|
+
() => target.abandonChanges(),
|
|
497
|
+
() => nativeDb.restartDefaultTxn(),
|
|
498
|
+
() => target.closeDb(),
|
|
499
|
+
() => nativeDb.dispose(),
|
|
500
|
+
() => target.openDb(targetFile, OpenMode.ReadWrite),
|
|
501
|
+
() => target.createDb(targetFile),
|
|
502
|
+
() => target.applyChangeset(file),
|
|
503
|
+
() => target.vacuum(),
|
|
504
|
+
() => nativeDb.analyze(),
|
|
505
|
+
() => nativeDb.enableWalMode(),
|
|
506
|
+
() => nativeDb.performCheckpoint(),
|
|
507
|
+
() => nativeDb.setAutoCheckpointThreshold(100),
|
|
508
|
+
() => target.executeDdl("DROP TABLE test1"),
|
|
509
|
+
() => target.startChangeTracking(),
|
|
510
|
+
() => target.createChangeset(file),
|
|
511
|
+
() => nativeDb.saveFileProperty({ namespace: "test", name: "test" }, "value"),
|
|
512
|
+
];
|
|
513
|
+
let calls = 0;
|
|
514
|
+
target.applyChangeset(file, applyFlags.None, () => {
|
|
515
|
+
++calls;
|
|
516
|
+
expect(readRows(target, "test1")[0]).deep.equal({ id: 1, val: "existing" });
|
|
517
|
+
for (const operation of operations)
|
|
518
|
+
expect(operation).throws("cannot modify or close database while applying a changeset");
|
|
519
|
+
expect(target.isOpen).true;
|
|
520
|
+
return DbConflictResolution.Replace;
|
|
521
|
+
});
|
|
522
|
+
expect(calls).equal(1);
|
|
523
|
+
target.saveChanges();
|
|
524
|
+
target.closeDb();
|
|
525
|
+
target.openDb(targetFile, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
526
|
+
expect(readRows(target, "test1")[0]).deep.equal({ id: 1, val: "incoming" });
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
it("should release the apply guard after a reentrant operation aborts the callback", () => {
|
|
530
|
+
withChangeset("reentrant-apply-failure", [
|
|
531
|
+
"UPDATE test1 SET val='incoming1' WHERE id=1",
|
|
532
|
+
"UPDATE test1 SET val='incoming2' WHERE id=2",
|
|
533
|
+
], ["UPDATE test1 SET val='existing' WHERE id=2"], (target, file) => {
|
|
534
|
+
expect(() => target.applyChangeset(file, applyFlags.None, () => {
|
|
535
|
+
target.saveChanges();
|
|
536
|
+
return DbConflictResolution.Replace;
|
|
537
|
+
})).throws("cannot modify or close database while applying a changeset");
|
|
538
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "incoming1" }, { id: 2, val: "existing" }]);
|
|
539
|
+
target.abandonChanges();
|
|
540
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "base1" }, { id: 2, val: "existing" }]);
|
|
541
|
+
target.applyChangeset(file, applyFlags.None, () => DbConflictResolution.Replace);
|
|
542
|
+
expect(readRows(target, "test1")).deep.equal([{ id: 1, val: "incoming1" }, { id: 2, val: "incoming2" }]);
|
|
543
|
+
});
|
|
544
|
+
});
|
|
545
|
+
for (const testCase of [conflictCases[1], conflictCases[5]]) {
|
|
546
|
+
it(`should reject Replace when the handler must ${testCase.name}`, () => {
|
|
547
|
+
withChangeset(`invalid-replace-${testCase.name}`, [testCase.sourceSql], [testCase.targetSql], (target, file) => {
|
|
548
|
+
const beforeApply = readRows(target, "test1");
|
|
549
|
+
expect(() => target.applyChangeset(file, applyFlags.None, () => DbConflictResolution.Replace))
|
|
550
|
+
.throws(TypeError, "Replace is valid only for data and primary-key insert conflicts");
|
|
551
|
+
target.abandonChanges();
|
|
552
|
+
expect(readRows(target, "test1")).deep.equal(beforeApply);
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
const withCascadeChangeset = (name, test) => {
|
|
557
|
+
const sourceFileName = IModelTestUtils.prepareOutputFile("SQLiteDb", `cascade-${name}-source.db`);
|
|
558
|
+
const targetFileName = IModelTestUtils.prepareOutputFile("SQLiteDb", `cascade-${name}-target.db`);
|
|
559
|
+
const changesetFileName = IModelTestUtils.prepareOutputFile("SQLiteDb", `cascade-${name}.changeset`);
|
|
560
|
+
const source = new SQLiteDb();
|
|
561
|
+
const target = new SQLiteDb();
|
|
562
|
+
try {
|
|
563
|
+
source.createDb(sourceFileName, undefined, { rawSQLite: true });
|
|
564
|
+
source.executeSQL("CREATE TABLE parent(id INTEGER PRIMARY KEY,val TEXT)");
|
|
565
|
+
source.executeSQL("CREATE TABLE child(id INTEGER PRIMARY KEY,val TEXT,parentId INTEGER REFERENCES parent(id) ON DELETE CASCADE)");
|
|
566
|
+
source.executeSQL("INSERT INTO parent VALUES (1,'slab')");
|
|
567
|
+
source.executeSQL("INSERT INTO child VALUES (1,'child1',1),(2,'child2',1)");
|
|
568
|
+
source.saveChanges();
|
|
569
|
+
source.closeDb();
|
|
570
|
+
IModelJsFs.copySync(sourceFileName, targetFileName);
|
|
571
|
+
source.openDb(sourceFileName, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
572
|
+
source.startChangeTracking();
|
|
573
|
+
source.executeSQL("DELETE FROM parent WHERE id=1");
|
|
574
|
+
expect(readRows(source, "parent")).deep.equal([]);
|
|
575
|
+
expect(readRows(source, "child")).deep.equal([]);
|
|
576
|
+
source.saveChanges();
|
|
577
|
+
source.createChangeset(changesetFileName);
|
|
578
|
+
target.openDb(targetFileName, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
579
|
+
test(target, changesetFileName, targetFileName);
|
|
580
|
+
}
|
|
581
|
+
finally {
|
|
582
|
+
if (source.isOpen)
|
|
583
|
+
source.closeDb();
|
|
584
|
+
if (target.isOpen)
|
|
585
|
+
target.closeDb();
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
it("should apply a captured parent cascade with FkNoAction and preserve unrelated families", () => {
|
|
589
|
+
withCascadeChangeset("unrelated-family", (target, file, targetFile) => {
|
|
590
|
+
target.executeSQL("INSERT INTO parent VALUES (2,'other')");
|
|
591
|
+
target.executeSQL("INSERT INTO child VALUES (3,'other child',2)");
|
|
592
|
+
target.saveChanges();
|
|
593
|
+
target.applyChangeset(file, applyFlags.FkNoAction);
|
|
594
|
+
expect(readRows(target, "parent")).deep.equal([{ id: 2, val: "other" }]);
|
|
595
|
+
expect(readRows(target, "child")).deep.equal([{ id: 3, val: "other child" }]);
|
|
596
|
+
target.abandonChanges();
|
|
597
|
+
expect(readRows(target, "parent")).deep.equal([{ id: 1, val: "slab" }, { id: 2, val: "other" }]);
|
|
598
|
+
expect(readRows(target, "child")).deep.equal([{ id: 1, val: "child1" }, { id: 2, val: "child2" }, { id: 3, val: "other child" }]);
|
|
599
|
+
target.applyChangeset(file, applyFlags.FkNoAction);
|
|
600
|
+
target.saveChanges();
|
|
601
|
+
target.closeDb();
|
|
602
|
+
target.openDb(targetFile, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
603
|
+
expect(readRows(target, "parent")).deep.equal([{ id: 2, val: "other" }]);
|
|
604
|
+
expect(readRows(target, "child")).deep.equal([{ id: 3, val: "other child" }]);
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
it("should apply a captured parent cascade with foreign-key actions enabled", () => {
|
|
608
|
+
withCascadeChangeset("actions-enabled", (target, file, targetFile) => {
|
|
609
|
+
target.applyChangeset(file, applyFlags.None, policy.NoLocalChanges);
|
|
610
|
+
expect(readRows(target, "parent")).deep.equal([]);
|
|
611
|
+
expect(readRows(target, "child")).deep.equal([]);
|
|
612
|
+
target.saveChanges();
|
|
613
|
+
target.closeDb();
|
|
614
|
+
target.openDb(targetFile, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
615
|
+
expect(readRows(target, "parent")).deep.equal([]);
|
|
616
|
+
expect(readRows(target, "child")).deep.equal([]);
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
it("should replace conflicting indirect child deletes from a captured parent cascade", () => {
|
|
620
|
+
withCascadeChangeset("changed-child", (target, file, targetFile) => {
|
|
621
|
+
target.executeSQL("UPDATE child SET val='changed child' WHERE id=1");
|
|
622
|
+
target.saveChanges();
|
|
623
|
+
expect(() => target.applyChangeset(file, applyFlags.FkNoAction)).throws();
|
|
624
|
+
target.abandonChanges();
|
|
625
|
+
expect(readRows(target, "parent")).deep.equal([{ id: 1, val: "slab" }]);
|
|
626
|
+
expect(readRows(target, "child")).deep.equal([{ id: 1, val: "changed child" }, { id: 2, val: "child2" }]);
|
|
627
|
+
const conflicts = [];
|
|
628
|
+
target.applyChangeset(file, applyFlags.FkNoAction, (args) => {
|
|
629
|
+
conflicts.push(args);
|
|
630
|
+
return DbConflictResolution.Replace;
|
|
631
|
+
});
|
|
632
|
+
expect(conflicts).deep.equal([{
|
|
633
|
+
cause: DbConflictCause.Data,
|
|
634
|
+
opcode: DbOpcode.Delete,
|
|
635
|
+
indirect: true,
|
|
636
|
+
tableName: "child",
|
|
637
|
+
columnCount: 3,
|
|
638
|
+
primaryKeyColumns: [0],
|
|
639
|
+
}]);
|
|
640
|
+
expect(readRows(target, "parent")).deep.equal([]);
|
|
641
|
+
expect(readRows(target, "child")).deep.equal([]);
|
|
642
|
+
target.abandonChanges();
|
|
643
|
+
expect(readRows(target, "child")).deep.equal([{ id: 1, val: "changed child" }, { id: 2, val: "child2" }]);
|
|
644
|
+
target.applyChangeset(file, applyFlags.FkNoAction, policy.NoLocalChanges);
|
|
645
|
+
target.saveChanges();
|
|
646
|
+
target.closeDb();
|
|
647
|
+
target.openDb(targetFile, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
648
|
+
expect(readRows(target, "parent")).deep.equal([]);
|
|
649
|
+
expect(readRows(target, "child")).deep.equal([]);
|
|
650
|
+
});
|
|
651
|
+
});
|
|
652
|
+
for (const extraChild of [false, true]) {
|
|
653
|
+
it(`should ${extraChild ? "reject orphaned children instead of cascading" : "apply parent and cascaded child deletions"} with FkNoAction and the no-local-changes policy`, () => {
|
|
654
|
+
withCascadeChangeset(extraChild ? "orphan" : "complete", (target, changesetFileName, targetFileName) => {
|
|
655
|
+
if (extraChild) {
|
|
656
|
+
target.executeSQL("INSERT INTO child VALUES (3,'extra child',1)");
|
|
657
|
+
target.saveChanges();
|
|
658
|
+
expect(() => target.applyChangeset(changesetFileName, applyFlags.FkNoAction, policy.NoLocalChanges)).throws();
|
|
659
|
+
target.abandonChanges();
|
|
660
|
+
expect(readRows(target, "parent")).deep.equal([{ id: 1, val: "slab" }]);
|
|
661
|
+
expect(readRows(target, "child")).deep.equal([{ id: 1, val: "child1" }, { id: 2, val: "child2" }, { id: 3, val: "extra child" }]);
|
|
662
|
+
const conflicts = [];
|
|
663
|
+
expect(() => target.applyChangeset(changesetFileName, applyFlags.FkNoAction, (args) => {
|
|
664
|
+
conflicts.push(args);
|
|
665
|
+
return args.cause === DbConflictCause.ForeignKey ? DbConflictResolution.Abort : DbConflictResolution.Skip;
|
|
666
|
+
})).throws();
|
|
667
|
+
expect(conflicts).deep.equal([{ cause: DbConflictCause.ForeignKey, foreignKeyConflicts: 1 }]);
|
|
668
|
+
target.abandonChanges();
|
|
669
|
+
expect(() => target.applyChangeset(changesetFileName, applyFlags.FkNoAction, () => DbConflictResolution.Replace))
|
|
670
|
+
.throws(TypeError, "Replace is valid only for data and primary-key insert conflicts");
|
|
671
|
+
target.abandonChanges();
|
|
672
|
+
target.applyChangeset(changesetFileName, applyFlags.None, policy.NoLocalChanges);
|
|
673
|
+
expect(readRows(target, "child")).deep.equal([]);
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
expect(() => target.applyChangeset(changesetFileName)).throws();
|
|
677
|
+
target.abandonChanges();
|
|
678
|
+
target.applyChangeset(changesetFileName, applyFlags.FkNoAction, policy.NoLocalChanges);
|
|
679
|
+
expect(readRows(target, "parent")).deep.equal([]);
|
|
680
|
+
expect(readRows(target, "child")).deep.equal([]);
|
|
681
|
+
target.saveChanges();
|
|
682
|
+
target.closeDb();
|
|
683
|
+
target.openDb(targetFileName, { openMode: OpenMode.ReadWrite, rawSQLite: true });
|
|
684
|
+
expect(readRows(target, "parent")).deep.equal([]);
|
|
685
|
+
expect(readRows(target, "child")).deep.equal([]);
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
}
|
|
269
690
|
});
|
|
270
691
|
});
|
|
271
692
|
//# sourceMappingURL=SQLiteDb.test.js.map
|