@itwin/core-backend 5.14.0-dev.3 → 5.14.0-dev.4

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.
Files changed (59) hide show
  1. package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
  2. package/lib/cjs/BriefcaseManager.js +11 -5
  3. package/lib/cjs/BriefcaseManager.js.map +1 -1
  4. package/lib/cjs/CloudSqlite.d.ts.map +1 -1
  5. package/lib/cjs/CloudSqlite.js +9 -2
  6. package/lib/cjs/CloudSqlite.js.map +1 -1
  7. package/lib/cjs/ECDb.d.ts.map +1 -1
  8. package/lib/cjs/ECDb.js +1 -0
  9. package/lib/cjs/ECDb.js.map +1 -1
  10. package/lib/cjs/IModelDb.d.ts +42 -7
  11. package/lib/cjs/IModelDb.d.ts.map +1 -1
  12. package/lib/cjs/IModelDb.js +175 -74
  13. package/lib/cjs/IModelDb.js.map +1 -1
  14. package/lib/cjs/SchemaSync.d.ts +126 -18
  15. package/lib/cjs/SchemaSync.d.ts.map +1 -1
  16. package/lib/cjs/SchemaSync.js +303 -81
  17. package/lib/cjs/SchemaSync.js.map +1 -1
  18. package/lib/cjs/TxnManager.d.ts.map +1 -1
  19. package/lib/cjs/TxnManager.js +2 -5
  20. package/lib/cjs/TxnManager.js.map +1 -1
  21. package/lib/cjs/internal/SchemaSyncReservations.d.ts.map +1 -1
  22. package/lib/cjs/internal/SchemaSyncReservations.js +13 -3
  23. package/lib/cjs/internal/SchemaSyncReservations.js.map +1 -1
  24. package/lib/esm/BriefcaseManager.d.ts.map +1 -1
  25. package/lib/esm/BriefcaseManager.js +11 -5
  26. package/lib/esm/BriefcaseManager.js.map +1 -1
  27. package/lib/esm/CloudSqlite.d.ts.map +1 -1
  28. package/lib/esm/CloudSqlite.js +10 -3
  29. package/lib/esm/CloudSqlite.js.map +1 -1
  30. package/lib/esm/ECDb.d.ts.map +1 -1
  31. package/lib/esm/ECDb.js +1 -0
  32. package/lib/esm/ECDb.js.map +1 -1
  33. package/lib/esm/IModelDb.d.ts +42 -7
  34. package/lib/esm/IModelDb.d.ts.map +1 -1
  35. package/lib/esm/IModelDb.js +175 -74
  36. package/lib/esm/IModelDb.js.map +1 -1
  37. package/lib/esm/SchemaSync.d.ts +126 -18
  38. package/lib/esm/SchemaSync.d.ts.map +1 -1
  39. package/lib/esm/SchemaSync.js +304 -82
  40. package/lib/esm/SchemaSync.js.map +1 -1
  41. package/lib/esm/TxnManager.d.ts.map +1 -1
  42. package/lib/esm/TxnManager.js +2 -5
  43. package/lib/esm/TxnManager.js.map +1 -1
  44. package/lib/esm/internal/SchemaSyncReservations.d.ts.map +1 -1
  45. package/lib/esm/internal/SchemaSyncReservations.js +13 -3
  46. package/lib/esm/internal/SchemaSyncReservations.js.map +1 -1
  47. package/lib/esm/test/hubaccess/Rebase.test.js +3 -1
  48. package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
  49. package/lib/esm/test/hubaccess/SemanticRebase.test.js +29 -28
  50. package/lib/esm/test/hubaccess/SemanticRebase.test.js.map +1 -1
  51. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.d.ts +3 -0
  52. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.d.ts.map +1 -0
  53. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.js +15 -0
  54. package/lib/esm/test/hubaccess/SemanticRebaseTestUtils.js.map +1 -0
  55. package/lib/esm/test/standalone/SchemaSyncDb.test.js +5 -25
  56. package/lib/esm/test/standalone/SchemaSyncDb.test.js.map +1 -1
  57. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js +1 -0
  58. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js.map +1 -1
  59. package/package.json +14 -14
@@ -1,8 +1,9 @@
1
1
  import { CloudSqlite } from "./CloudSqlite";
2
2
  import { SQLiteDb, VersionedSqliteDb } from "./SQLiteDb";
3
- import { IModelDb } from "./IModelDb";
3
+ import { BriefcaseDb, IModelDb } from "./IModelDb";
4
4
  import { GuidString, Id64String, OpenMode } from "@itwin/core-bentley";
5
5
  import { Code, LocalFileName } from "@itwin/core-common";
6
+ import type { BlobContainer } from "./BlobContainerService";
6
7
  /** @internal */
7
8
  export declare namespace SchemaSync {
8
9
  /** Identifies an element to be reserved in a `SchemaSyncDb`. @internal */
@@ -21,13 +22,13 @@ export declare namespace SchemaSync {
21
22
  findReservedElement(federationGuid: GuidString): ReservedElement | undefined;
22
23
  }
23
24
  interface WriteMethods {
24
- /** Reserve the specified elements in the `SchemaSyncDb`. Throws if any of the requested reservations conflict with existing reservations. */
25
+ /** Reserve the specified elements in the `SchemaSyncDb`. Throws if any requested reservation conflicts with an existing reservation. */
25
26
  reserveElements(identities: ProposedElementReservation[]): Promise<void>;
26
27
  }
27
28
  /** A CloudSqlite database for synchronizing schema changes across briefcases. */
28
29
  class SchemaSyncDb extends VersionedSqliteDb implements ReadMethods, WriteMethods {
29
30
  private _supportsReservations?;
30
- readonly myVersion = "4.1.0";
31
+ readonly myVersion = "5.0.0";
31
32
  protected createDDL(): void;
32
33
  openDb(dbName: string, openMode: OpenMode | SQLiteDb.OpenParams, container?: CloudSqlite.CloudContainer): void;
33
34
  private ensureReservedElementsTable;
@@ -38,28 +39,133 @@ export declare namespace SchemaSync {
38
39
  private getNextReservedElementLocalId;
39
40
  private setNextReservedElementLocalId;
40
41
  }
41
- const setTestCache: (iModel: IModelDb, cacheName?: string) => void;
42
- const getCloudAccess: (arg: IModelDb | {
42
+ /** The `containerType` recorded on a BlobContainer that holds an iModel's `SchemaSyncDb`. */
43
+ const containerType = "schema-sync";
44
+ function setTestCache(iModel: IModelDb, cacheName?: string): void;
45
+ /** Either an open iModel or the name of a closed briefcase file. Every local read below accepts both, so
46
+ * a caller that has only a file name does not have to open a `BriefcaseDb` to ask a question about it.
47
+ */
48
+ type IModelOrFileName = IModelDb | {
43
49
  readonly fileName: LocalFileName;
44
- }) => Promise<CloudAccess>;
45
- const withLockedAccess: (iModel: IModelDb | {
46
- readonly fileName: LocalFileName;
47
- }, args: {
50
+ };
51
+ /** Whether this iModel's ECSchemas are governed by a `SchemaSyncDb`.
52
+ *
53
+ * This is the single question every schema operation branches on. It reads one `be_Prop` row written when
54
+ * schema sync was enabled and carried to every other briefcase by that changeset - no cloud access, and it
55
+ * works on a readonly briefcase, a checkpoint, or a closed file.
56
+ * @note This says nothing about whether the container is reachable, or even named. Use
57
+ * [[queryContainerProps]] for that. The two are separate on purpose: a file that says it is governed by a
58
+ * sync db must never fall back to importing schemas on its own, whatever state the container is in.
59
+ */
60
+ function isEnabled(arg: IModelOrFileName): boolean;
61
+ /** The container holding this iModel's `SchemaSyncDb`, or `undefined` if schema sync was never enabled.
62
+ *
63
+ * Local read of the same `be_Prop` file property that [[initializeForIModel]] wrote. Requesting an access
64
+ * token for the container is a separate, asynchronous step.
65
+ */
66
+ function queryContainerProps(arg: IModelOrFileName): CloudSqlite.ContainerProps | undefined;
67
+ function getCloudAccess(arg: IModelOrFileName): Promise<CloudAccess>;
68
+ /** Release an access obtained through [[getCloudAccess]]. */
69
+ function releaseCloudAccess(access: CloudAccess): void;
70
+ /** Arguments for [[withLockedAccess]]. */
71
+ interface WithLockedAccessArgs {
48
72
  operationName: string;
49
73
  openMode?: OpenMode;
50
74
  user?: string;
51
- }, operation: (access: CloudAccess) => Promise<void>) => Promise<void>;
52
- const withReadonlyAccess: (iModel: IModelDb | {
53
- readonly fileName: LocalFileName;
54
- }, operation: (access: CloudAccess) => Promise<void>) => Promise<void>;
55
- const isEnabled: (iModel: IModelDb) => boolean;
56
- /** Synchronize local briefcase schemas with cloud container */
57
- const pull: (iModel: IModelDb) => Promise<void>;
58
- const initializeForIModel: (arg: {
75
+ }
76
+ function withLockedAccess(iModel: IModelOrFileName, args: WithLockedAccessArgs, operation: (access: CloudAccess) => Promise<void>): Promise<void>;
77
+ /** Build the tables and indexes the briefcase's `ec_` rows describe. A merged schema changeset carries
78
+ * those rows but no DDL, so the physical columns are missing until this runs. Needs no cloud access.
79
+ */
80
+ function updateDbSchema(iModel: IModelDb): void;
81
+ /** Whether a failed [[IModelDb.importSchemas]] can be retried through the upgrade path.
82
+ *
83
+ * The update tier refuses two kinds of change: one that has to move data between columns, and one
84
+ * that destroys instances or property values. They arrive as different statuses because they are
85
+ * different changes, but they mean one thing to a caller - this needs
86
+ * [[BriefcaseDb.upgradeSchemas]], which takes the exclusive schema lock.
87
+ *
88
+ * The retry is the app's decision, not the platform's: taking that lock disturbs everyone else, so
89
+ * only the app knows whether to do it now, schedule it, or tell the user.
90
+ * ```ts
91
+ * try {
92
+ * await db.importSchemas(files);
93
+ * } catch (e) {
94
+ * if (!SchemaSync.requiresUpgrade(e)) throw e;
95
+ * await db.upgradeSchemas(files, { description: "..." });
96
+ * }
97
+ * ```
98
+ * @internal
99
+ */
100
+ function requiresUpgrade(error: unknown): boolean;
101
+ /** Arguments for [[createContainerForIModel]]. */
102
+ interface CreateContainerForIModelArgs {
103
+ iModel: IModelDb;
104
+ label?: string;
105
+ description?: string;
106
+ }
107
+ /** Create a cloud container to hold this iModel's `SchemaSyncDb`, and initialize it as empty.
108
+ *
109
+ * The container is scoped to the iModel, so the service deletes it when the iModel is deleted. Pass the
110
+ * returned props to [[initializeForIModel]] - the two calls together are what enables schema sync on an
111
+ * iModel that was created without it.
112
+ * @note The current user must be authorized to create containers for the iTwin.
113
+ */
114
+ function createContainerForIModel(arg: CreateContainerForIModelArgs): Promise<CloudSqlite.ContainerProps>;
115
+ /** Arguments for [[initializeForIModel]]. */
116
+ interface InitializeForIModelArgs {
59
117
  iModel: IModelDb;
60
118
  containerProps: CloudSqlite.ContainerProps;
119
+ /** Replace the container already recorded on this iModel instead of failing. */
61
120
  overrideContainer?: boolean;
62
- }) => Promise<void>;
121
+ }
122
+ /** Enable schema sync for an iModel, seeding the container from this briefcase.
123
+ * @note Takes the exclusive schema lock, refuses local changes, and pulls the briefcase to the tip before writing the container.
124
+ * If this operation fails after acquiring the schema lock, it leaves the lock held.
125
+ */
126
+ function initializeForIModel(arg: InitializeForIModelArgs): Promise<void>;
127
+ /** The part of a SchemaSyncDb to restore from an authoritative briefcase. @alpha */
128
+ type RepairScope = "schemaMetadata" | "schemaMetadataAndProfile";
129
+ /** Arguments for [[repairForIModel]]. @alpha */
130
+ interface RepairForIModelArgs {
131
+ /** A clean SchemaSync-enabled briefcase at the tip of the iModel timeline. */
132
+ iModel: BriefcaseDb;
133
+ /** Include the schema-owned EC and BeSQLite profile table definitions. */
134
+ scope?: RepairScope;
135
+ }
136
+ /** Restore the schema-owned portion of the SchemaSyncDb from a briefcase at the tip of the timeline.
137
+ *
138
+ * This operation takes the exclusive schema lock and refuses to pull or modify the briefcase. Element
139
+ * reservations and all other target-only state in the SchemaSyncDb are preserved.
140
+ * @alpha
141
+ */
142
+ function repairForIModel(arg: RepairForIModelArgs): Promise<void>;
143
+ /** Arguments for [[enableForIModel]]. */
144
+ interface EnableForIModelArgs {
145
+ iModel: IModelDb;
146
+ /** An existing container to use. When omitted one is created for this iModel. */
147
+ containerProps?: CloudSqlite.ContainerProps;
148
+ /** Replace the container already recorded on this iModel instead of failing. */
149
+ overrideContainer?: boolean;
150
+ label?: string;
151
+ description?: string;
152
+ }
153
+ /** Turn schema sync on for an iModel: create the container if needed, record it, and seed it.
154
+ *
155
+ * The single call callers should use. [[createContainerForIModel]] and [[initializeForIModel]] have
156
+ * to happen in this order, and an iModel left between the two is one that names a container nothing
157
+ * has seeded.
158
+ * @returns the container props recorded on the iModel.
159
+ * @note Takes the exclusive schema lock and pushes, same protocol as [[BriefcaseDb.upgradeSchemas]] -
160
+ * every operation that changes how a file is governed uses it. If this operation fails after acquiring the schema lock,
161
+ * it leaves the lock held.
162
+ */
163
+ function enableForIModel(arg: EnableForIModelArgs): Promise<CloudSqlite.ContainerProps>;
164
+ /** Arguments for [[CloudAccess.createNewContainer]]. */
165
+ interface CreateNewContainerProps {
166
+ scope: BlobContainer.Scope;
167
+ metadata: Omit<BlobContainer.Metadata, "containerType">;
168
+ }
63
169
  /** Provides access to a cloud-based `SchemaSyncDb` to hold ECSchemas. */
64
170
  class CloudAccess extends CloudSqlite.DbAccess<SchemaSyncDb, ReadMethods, WriteMethods> {
65
171
  constructor(props: CloudSqlite.ContainerAccessProps);
@@ -70,6 +176,8 @@ export declare namespace SchemaSync {
70
176
  * @note this deletes any existing content in the container.
71
177
  */
72
178
  static initializeDb(props: CloudSqlite.ContainerProps): Promise<void>;
179
+ /** Create and initialize a new `schema-sync` BlobContainer to hold a `SchemaSyncDb`. */
180
+ static createNewContainer(args: CreateNewContainerProps): Promise<CloudSqlite.ContainerProps>;
73
181
  }
74
182
  }
75
183
  //# sourceMappingURL=SchemaSync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaSync.d.ts","sourceRoot":"","sources":["../../src/SchemaSync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SchemaSync.d.ts","sourceRoot":"","sources":["../../src/SchemaSync.ts"],"names":[],"mappings":"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"}
@@ -19,14 +19,27 @@ const Symbols_1 = require("./internal/Symbols");
19
19
  /** @internal */
20
20
  var SchemaSync;
21
21
  (function (SchemaSync) {
22
- const lockParams = { retryDelayMs: 1000, nRetries: 30 };
22
+ /** How long a briefcase waits for another briefcase's schema import to finish before giving up.
23
+ *
24
+ * `nRetries` alone does not bound the wait: when the count runs out `CloudSqlite.withLockedDb` asks
25
+ * `onFailure` what to do, and anything other than "stop" restarts the whole cycle. Without an
26
+ * `onFailure` the caller waits forever with no way to give up, so this supplies one.
27
+ */
28
+ const lockParams = {
29
+ retryDelayMs: 6000,
30
+ nRetries: 10,
31
+ onFailure: async (lockedBy, expires) => {
32
+ core_bentley_1.Logger.logInfo("SchemaSync", `schema sync container is held by ${lockedBy} until ${expires}; giving up`);
33
+ return "stop";
34
+ },
35
+ };
23
36
  const reservedElementsTableName = "reserved_elements";
24
37
  const maxLocalIdExclusive = 0x10000000000; // 2^40
25
38
  const idSequenceProp = { namespace: "schemasync", name: "nextReservedElementLocalId" };
26
39
  /** A CloudSqlite database for synchronizing schema changes across briefcases. */
27
40
  class SchemaSyncDb extends SQLiteDb_1.VersionedSqliteDb {
28
41
  _supportsReservations;
29
- myVersion = "4.1.0";
42
+ myVersion = "5.0.0";
30
43
  createDDL() {
31
44
  this.ensureReservedElementsTable();
32
45
  }
@@ -138,116 +151,319 @@ var SchemaSync;
138
151
  SchemaSync.SchemaSyncDb = SchemaSyncDb;
139
152
  const syncProperty = { namespace: "itwinjs", name: "SchemaSync" };
140
153
  const defaultDbName = "SchemaSyncDb";
154
+ /** The `containerType` recorded on a BlobContainer that holds an iModel's `SchemaSyncDb`. */
155
+ SchemaSync.containerType = "schema-sync";
141
156
  const testSyncCachePropKey = "test.schema_sync.cache_name";
142
157
  // for tests only
143
- SchemaSync.setTestCache = (iModel, cacheName) => {
158
+ function setTestCache(iModel, cacheName) {
144
159
  if (cacheName)
145
160
  iModel[Symbols_1._nativeDb].saveLocalValue(testSyncCachePropKey, cacheName);
146
161
  else
147
162
  iModel[Symbols_1._nativeDb].deleteLocalValue(testSyncCachePropKey);
148
- };
149
- const sharedAccessByIModel = new Map();
150
- SchemaSync.getCloudAccess = async (arg) => {
151
- let nativeDb;
152
- const argIsIModelDb = arg instanceof IModelDb_1.IModelDb;
153
- if (argIsIModelDb) {
154
- nativeDb = arg[Symbols_1._nativeDb];
155
- }
156
- else {
157
- nativeDb = new NativePlatform_1.IModelNative.platform.DgnDb();
158
- nativeDb.openIModel(arg.fileName, core_bentley_1.OpenMode.Readonly);
159
- }
160
- const testSyncCache = nativeDb.queryLocalValue(testSyncCachePropKey);
161
- const propsString = nativeDb.queryFileProperty(syncProperty, true);
162
- if (!propsString)
163
- throw new Error("iModel does not have a SchemaSyncDb");
163
+ }
164
+ SchemaSync.setTestCache = setTestCache;
165
+ /** Read from an open iModel, or from a closed file opened readonly for the duration of `operation`. */
166
+ function readFromIModel(arg, operation) {
167
+ if (arg instanceof IModelDb_1.IModelDb)
168
+ return operation(arg[Symbols_1._nativeDb]);
169
+ const nativeDb = new NativePlatform_1.IModelNative.platform.DgnDb();
170
+ nativeDb.openIModel(arg.fileName, core_bentley_1.OpenMode.Readonly);
164
171
  try {
165
- // Reuse the existing access for this container so there is only ever one CloudAccess per container (per cache).
166
- const sharedAccessKey = propsString + (testSyncCache ?? "");
167
- const cached = sharedAccessByIModel.get(sharedAccessKey);
168
- if (cached)
169
- return cached;
170
- const props = JSON.parse(propsString);
171
- const accessToken = await CloudSqlite_1.CloudSqlite.requestToken(props);
172
- const access = new CloudAccess({ ...props, accessToken });
173
- Object.assign(access.lockParams, lockParams);
174
- if (testSyncCache)
175
- access.setCache(CloudSqlite_1.CloudSqlite.CloudCaches.getCache({ cacheName: testSyncCache }));
176
- sharedAccessByIModel.set(sharedAccessKey, access);
177
- return access;
172
+ return operation(nativeDb);
178
173
  }
179
174
  finally {
180
- if (!argIsIModelDb) {
181
- nativeDb.closeFile();
175
+ nativeDb.closeFile();
176
+ }
177
+ }
178
+ /** Whether this iModel's ECSchemas are governed by a `SchemaSyncDb`.
179
+ *
180
+ * This is the single question every schema operation branches on. It reads one `be_Prop` row written when
181
+ * schema sync was enabled and carried to every other briefcase by that changeset - no cloud access, and it
182
+ * works on a readonly briefcase, a checkpoint, or a closed file.
183
+ * @note This says nothing about whether the container is reachable, or even named. Use
184
+ * [[queryContainerProps]] for that. The two are separate on purpose: a file that says it is governed by a
185
+ * sync db must never fall back to importing schemas on its own, whatever state the container is in.
186
+ */
187
+ function isEnabled(arg) {
188
+ return readFromIModel(arg, (nativeDb) => nativeDb.schemaSyncEnabled());
189
+ }
190
+ SchemaSync.isEnabled = isEnabled;
191
+ /** One pass over the values schema sync keeps in the file: which container holds the sync db, plus the
192
+ * test-only cache override. Read together so a closed file is opened once.
193
+ */
194
+ function readLocalSyncProps(arg) {
195
+ return readFromIModel(arg, (nativeDb) => {
196
+ const propsString = nativeDb.queryFileProperty(syncProperty, true);
197
+ let containerProps;
198
+ if (propsString !== undefined) {
199
+ try {
200
+ containerProps = JSON.parse(propsString);
201
+ }
202
+ catch (e) {
203
+ // Deliberately not swallowed into `undefined`: that would read as "schema sync was never
204
+ // enabled" and let this briefcase import schemas on its own.
205
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_CORRUPT, `iModel names a SchemaSyncDb container but the property cannot be read: ${core_bentley_1.BentleyError.getErrorMessage(e)}`);
206
+ }
207
+ }
208
+ return {
209
+ containerProps,
210
+ testCacheName: nativeDb.queryLocalValue(testSyncCachePropKey),
211
+ };
212
+ });
213
+ }
214
+ /** The container holding this iModel's `SchemaSyncDb`, or `undefined` if schema sync was never enabled.
215
+ *
216
+ * Local read of the same `be_Prop` file property that [[initializeForIModel]] wrote. Requesting an access
217
+ * token for the container is a separate, asynchronous step.
218
+ */
219
+ function queryContainerProps(arg) {
220
+ return readLocalSyncProps(arg).containerProps;
221
+ }
222
+ SchemaSync.queryContainerProps = queryContainerProps;
223
+ const sharedAccessByContainer = new Map();
224
+ const sharedAccessByInstance = new WeakMap();
225
+ async function getCloudAccess(arg) {
226
+ const { containerProps, testCacheName } = readLocalSyncProps(arg);
227
+ if (undefined === containerProps)
228
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_NOTFOUND, "iModel does not have a SchemaSyncDb");
229
+ const sharedAccessKey = JSON.stringify(containerProps) + (testCacheName ?? "");
230
+ let shared = sharedAccessByContainer.get(sharedAccessKey);
231
+ if (undefined === shared) {
232
+ const pending = (async () => {
233
+ const accessToken = await CloudSqlite_1.CloudSqlite.requestToken(containerProps);
234
+ const access = new CloudAccess({ ...containerProps, accessToken });
235
+ Object.assign(access.lockParams, lockParams);
236
+ if (testCacheName)
237
+ access.setCache(CloudSqlite_1.CloudSqlite.CloudCaches.getCache({ cacheName: testCacheName }));
238
+ const created = { access, key: sharedAccessKey, referenceCount: 0 };
239
+ sharedAccessByInstance.set(access, created);
240
+ return created;
241
+ })();
242
+ sharedAccessByContainer.set(sharedAccessKey, pending);
243
+ try {
244
+ shared = await pending;
245
+ if (sharedAccessByContainer.get(sharedAccessKey) === pending)
246
+ sharedAccessByContainer.set(sharedAccessKey, shared);
247
+ }
248
+ catch (error) {
249
+ if (sharedAccessByContainer.get(sharedAccessKey) === pending)
250
+ sharedAccessByContainer.delete(sharedAccessKey);
251
+ throw error;
182
252
  }
183
253
  }
184
- };
185
- SchemaSync.withLockedAccess = async (iModel, args, operation) => {
186
- const access = await SchemaSync.getCloudAccess(iModel);
187
- try {
188
- await access.withLockedDb(args, async () => operation(access));
254
+ else if (shared instanceof Promise) {
255
+ shared = await shared;
189
256
  }
190
- finally {
257
+ ++shared.referenceCount;
258
+ return shared.access;
259
+ }
260
+ SchemaSync.getCloudAccess = getCloudAccess;
261
+ /** Release an access obtained through [[getCloudAccess]]. */
262
+ function releaseCloudAccess(access) {
263
+ const shared = sharedAccessByInstance.get(access);
264
+ if (undefined === shared) {
191
265
  access.close();
266
+ return;
192
267
  }
193
- };
194
- SchemaSync.withReadonlyAccess = async (iModel, operation) => {
268
+ if (--shared.referenceCount > 0)
269
+ return;
270
+ sharedAccessByInstance.delete(access);
271
+ if (sharedAccessByContainer.get(shared.key) === shared)
272
+ sharedAccessByContainer.delete(shared.key);
273
+ access.close();
274
+ }
275
+ SchemaSync.releaseCloudAccess = releaseCloudAccess;
276
+ async function withLockedAccess(iModel, args, operation) {
195
277
  const access = await SchemaSync.getCloudAccess(iModel);
196
- access.synchronizeWithCloud();
197
- access.openForRead();
198
278
  try {
199
- await operation(access);
279
+ await access.withLockedDb(args, async () => operation(access));
200
280
  }
201
281
  finally {
202
- access.close();
282
+ releaseCloudAccess(access);
203
283
  }
204
- };
205
- SchemaSync.isEnabled = (iModel) => {
206
- return iModel[Symbols_1._nativeDb].schemaSyncEnabled();
207
- };
208
- /** Synchronize local briefcase schemas with cloud container */
209
- SchemaSync.pull = async (iModel) => {
210
- if (iModel[Symbols_1._nativeDb].schemaSyncEnabled() && !iModel.isReadonly) {
211
- await SchemaSync.withReadonlyAccess(iModel, async (syncAccess) => {
212
- const schemaSyncDbUri = syncAccess.getUri();
213
- iModel.clearCaches();
214
- iModel[Symbols_1._nativeDb].schemaSyncPull(schemaSyncDbUri);
215
- iModel[Symbols_1._implicitTxn].saveChanges("schema synchronized with cloud container");
216
- });
284
+ }
285
+ SchemaSync.withLockedAccess = withLockedAccess;
286
+ /** Build the tables and indexes the briefcase's `ec_` rows describe. A merged schema changeset carries
287
+ * those rows but no DDL, so the physical columns are missing until this runs. Needs no cloud access.
288
+ */
289
+ function updateDbSchema(iModel) {
290
+ if (isEnabled(iModel) && !iModel.isReadonly) {
291
+ iModel.clearCaches();
292
+ iModel[Symbols_1._nativeDb].schemaSyncUpdateDbSchema();
293
+ iModel[Symbols_1._implicitTxn].saveChanges("materialized db schema from ec_ tables");
217
294
  }
218
- };
219
- SchemaSync.initializeForIModel = async (arg) => {
220
- const props = { baseUri: arg.containerProps.baseUri, containerId: arg.containerProps.containerId, storageType: arg.containerProps.storageType }; // sanitize to only known properties
295
+ }
296
+ SchemaSync.updateDbSchema = updateDbSchema;
297
+ /** Whether a failed [[IModelDb.importSchemas]] can be retried through the upgrade path.
298
+ *
299
+ * The update tier refuses two kinds of change: one that has to move data between columns, and one
300
+ * that destroys instances or property values. They arrive as different statuses because they are
301
+ * different changes, but they mean one thing to a caller - this needs
302
+ * [[BriefcaseDb.upgradeSchemas]], which takes the exclusive schema lock.
303
+ *
304
+ * The retry is the app's decision, not the platform's: taking that lock disturbs everyone else, so
305
+ * only the app knows whether to do it now, schedule it, or tell the user.
306
+ * ```ts
307
+ * try {
308
+ * await db.importSchemas(files);
309
+ * } catch (e) {
310
+ * if (!SchemaSync.requiresUpgrade(e)) throw e;
311
+ * await db.upgradeSchemas(files, { description: "..." });
312
+ * }
313
+ * ```
314
+ * @internal
315
+ */
316
+ function requiresUpgrade(error) {
317
+ const errorNumber = error?.errorNumber;
318
+ return errorNumber === core_bentley_1.DbResult.BE_SQLITE_ERROR_DataTransformRequired
319
+ || errorNumber === core_bentley_1.DbResult.BE_SQLITE_ERROR_DataDeletionRequired;
320
+ }
321
+ SchemaSync.requiresUpgrade = requiresUpgrade;
322
+ /** Create a cloud container to hold this iModel's `SchemaSyncDb`, and initialize it as empty.
323
+ *
324
+ * The container is scoped to the iModel, so the service deletes it when the iModel is deleted. Pass the
325
+ * returned props to [[initializeForIModel]] - the two calls together are what enables schema sync on an
326
+ * iModel that was created without it.
327
+ * @note The current user must be authorized to create containers for the iTwin.
328
+ */
329
+ async function createContainerForIModel(arg) {
221
330
  const iModel = arg.iModel;
331
+ const iTwinId = iModel.iTwinId;
332
+ if (undefined === iTwinId)
333
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Cannot create a SchemaSync container for an iModel that has no iTwin");
334
+ return CloudAccess.createNewContainer({
335
+ scope: { iTwinId, iModelId: iModel.iModelId },
336
+ metadata: { label: arg.label ?? `SchemaSync for ${iModel.name}`, description: arg.description },
337
+ });
338
+ }
339
+ SchemaSync.createContainerForIModel = createContainerForIModel;
340
+ /** Refuse initialization before a caller provisions a container, then hold the exclusive schema lock and pull to the tip. */
341
+ async function prepareToInitializeForIModel(iModel, overrideContainer) {
222
342
  const briefcase = iModel instanceof IModelDb_1.BriefcaseDb ? iModel : undefined;
343
+ if (briefcase && !briefcase.locks.isServerBased)
344
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Cannot enable SchemaSync without server-based locking");
345
+ if (briefcase && (iModel[Symbols_1._nativeDb].hasUnsavedChanges() || briefcase.txns.hasLocalChanges))
346
+ throw new core_common_1.IModelError(core_bentley_1.ChangeSetStatus.HasLocalChanges, "Cannot enable SchemaSync while there are local changes");
347
+ const assertContainerCanBeInitialized = () => {
348
+ const localInfo = iModel[Symbols_1._nativeDb].schemaSyncGetLocalDbInfo();
349
+ if (localInfo && !overrideContainer)
350
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, `Local db already initialized to schema sync (container-id: ${localInfo.id})`);
351
+ };
352
+ assertContainerCanBeInitialized();
223
353
  await iModel.acquireSchemaLock();
224
- if (briefcase) {
225
- if (briefcase.txns.hasLocalChanges) {
226
- throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Enabling SchemaSync for iModel failed. There are unsaved or un-pushed local changes.");
227
- }
228
- await briefcase.pullChanges();
229
- }
354
+ await briefcase?.pullChanges();
355
+ assertContainerCanBeInitialized();
356
+ return briefcase;
357
+ }
358
+ async function initializeAfterPreflight(arg, briefcase) {
359
+ const props = { baseUri: arg.containerProps.baseUri, containerId: arg.containerProps.containerId, storageType: arg.containerProps.storageType }; // sanitize to only known properties
360
+ const iModel = arg.iModel;
361
+ const description = arg.overrideContainer
362
+ ? `Overriding SchemaSync for iModel with container-id: ${props.containerId}`
363
+ : `Enable SchemaSync for iModel with container-id: ${props.containerId}`;
364
+ const txnBeforeInit = briefcase?.txns.getCurrentTxnId();
230
365
  try {
231
366
  iModel[Symbols_1._implicitTxn].saveFileProperty(syncProperty, JSON.stringify(props));
232
- await SchemaSync.withLockedAccess(arg.iModel, { operationName: "initialize schemaSync", openMode: core_bentley_1.OpenMode.Readonly }, async (syncAccess) => {
233
- iModel[Symbols_1._nativeDb].schemaSyncInit(syncAccess.getUri(), props.containerId, arg.overrideContainer ?? false);
234
- iModel[Symbols_1._implicitTxn].saveChanges(`Enable SchemaSync (container id: ${props.containerId})`);
235
- });
236
- }
237
- catch (err) {
238
- throw err;
367
+ try {
368
+ await withLockedAccess(iModel, { operationName: "initialize schemaSync", openMode: core_bentley_1.OpenMode.Readonly }, async (syncAccess) => {
369
+ iModel[Symbols_1._nativeDb].schemaSyncInit(syncAccess.getUri(), props.containerId, arg.overrideContainer ?? false);
370
+ iModel[Symbols_1._implicitTxn].saveChanges(description);
371
+ });
372
+ }
373
+ catch (error) {
374
+ iModel[Symbols_1._implicitTxn].abandonChanges();
375
+ if (briefcase && txnBeforeInit !== undefined && briefcase.txns.getCurrentTxnId() !== txnBeforeInit) {
376
+ const status = briefcase[Symbols_1._nativeDb].cancelTo(txnBeforeInit, true);
377
+ if (status !== core_bentley_1.IModelStatus.Success)
378
+ core_bentley_1.Logger.logError("SchemaSync", `Failed to roll back schema sync initialization after the container upload failed: ${core_bentley_1.IModelStatus[status] ?? status}`);
379
+ }
380
+ throw error;
381
+ }
382
+ // Upload the initialized container before publishing the property that tells every briefcase to use it.
383
+ // If the push fails, this briefcase keeps the local txn and exclusive schema lock so the push can be retried.
384
+ await briefcase?.pushChanges({ description });
239
385
  }
240
386
  finally {
241
387
  iModel[Symbols_1._implicitTxn].abandonChanges();
242
388
  }
243
- if (briefcase) {
244
- if (arg.overrideContainer)
245
- await briefcase.pushChanges({ description: `Overriding SchemaSync for iModel with container-id: ${props.containerId}` });
246
- else
247
- await briefcase.pushChanges({ description: `Enable SchemaSync for iModel with container-id: ${props.containerId}` });
248
- }
249
389
  await iModel.initializeSharedElementReservations();
390
+ }
391
+ /** Enable schema sync for an iModel, seeding the container from this briefcase.
392
+ * @note Takes the exclusive schema lock, refuses local changes, and pulls the briefcase to the tip before writing the container.
393
+ * If this operation fails after acquiring the schema lock, it leaves the lock held.
394
+ */
395
+ async function initializeForIModel(arg) {
396
+ const briefcase = await prepareToInitializeForIModel(arg.iModel, arg.overrideContainer ?? false);
397
+ await initializeAfterPreflight(arg, briefcase);
398
+ }
399
+ SchemaSync.initializeForIModel = initializeForIModel;
400
+ const nativeRepairScope = {
401
+ schemaMetadata: 0,
402
+ schemaMetadataAndProfile: 1,
250
403
  };
404
+ /** Restore the schema-owned portion of the SchemaSyncDb from a briefcase at the tip of the timeline.
405
+ *
406
+ * This operation takes the exclusive schema lock and refuses to pull or modify the briefcase. Element
407
+ * reservations and all other target-only state in the SchemaSyncDb are preserved.
408
+ * @alpha
409
+ */
410
+ async function repairForIModel(arg) {
411
+ const iModel = arg.iModel;
412
+ if (!isEnabled(iModel))
413
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Cannot repair SchemaSync because it is not enabled for this iModel");
414
+ if (iModel[Symbols_1._nativeDb].hasUnsavedChanges() || iModel.txns.hasLocalChanges)
415
+ throw new core_common_1.IModelError(core_bentley_1.ChangeSetStatus.HasLocalChanges, "Cannot repair SchemaSync while there are local changes");
416
+ if (!iModel.locks.isServerBased)
417
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Cannot repair SchemaSync without server-based locking");
418
+ if (arg.scope !== undefined && arg.scope !== "schemaMetadata" && arg.scope !== "schemaMetadataAndProfile")
419
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Unknown SchemaSync repair scope");
420
+ const briefcaseManagerModule = await Promise.resolve().then(() => require("./BriefcaseManager"));
421
+ const assertBriefcaseIsAtTip = async () => {
422
+ const latestChangeset = await briefcaseManagerModule.BriefcaseManager.getLatestChangeset({ iModelId: iModel.iModelId });
423
+ if (latestChangeset.id !== iModel.changeset.id)
424
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Cannot repair SchemaSync from a briefcase that is not at the tip of the iModel timeline");
425
+ };
426
+ // Avoid asking Hub for an exclusive lock it cannot grant to a briefcase that first needs to pull.
427
+ await assertBriefcaseIsAtTip();
428
+ const schemaLockWasHeld = iModel.holdsSchemaLock;
429
+ if (!schemaLockWasHeld)
430
+ await iModel.acquireSchemaLock();
431
+ try {
432
+ // Close the race between the preflight query and acquiring the exclusive lock.
433
+ await assertBriefcaseIsAtTip();
434
+ const repairScope = nativeRepairScope[arg.scope ?? "schemaMetadata"];
435
+ await withLockedAccess(iModel, { openMode: core_bentley_1.OpenMode.Readonly, operationName: "repair schema sync" }, async (syncAccess) => {
436
+ const nativeDb = iModel[Symbols_1._nativeDb];
437
+ nativeDb.schemaSyncRepair(syncAccess.getUri(), repairScope);
438
+ });
439
+ core_bentley_1.Logger.logInfo("SchemaSync", `Repaired SchemaSyncDb from changeset ${iModel.changeset.id || "0"}`);
440
+ }
441
+ finally {
442
+ if (!schemaLockWasHeld)
443
+ await iModel.locks.abandonAllLocks();
444
+ }
445
+ }
446
+ SchemaSync.repairForIModel = repairForIModel;
447
+ /** Turn schema sync on for an iModel: create the container if needed, record it, and seed it.
448
+ *
449
+ * The single call callers should use. [[createContainerForIModel]] and [[initializeForIModel]] have
450
+ * to happen in this order, and an iModel left between the two is one that names a container nothing
451
+ * has seeded.
452
+ * @returns the container props recorded on the iModel.
453
+ * @note Takes the exclusive schema lock and pushes, same protocol as [[BriefcaseDb.upgradeSchemas]] -
454
+ * every operation that changes how a file is governed uses it. If this operation fails after acquiring the schema lock,
455
+ * it leaves the lock held.
456
+ */
457
+ async function enableForIModel(arg) {
458
+ if (undefined === arg.containerProps && undefined === arg.iModel.iTwinId)
459
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "Cannot create a SchemaSync container for an iModel that has no iTwin");
460
+ const briefcase = await prepareToInitializeForIModel(arg.iModel, arg.overrideContainer ?? false);
461
+ const containerProps = arg.containerProps
462
+ ?? await createContainerForIModel({ iModel: arg.iModel, label: arg.label, description: arg.description });
463
+ await initializeAfterPreflight({ iModel: arg.iModel, containerProps, overrideContainer: arg.overrideContainer }, briefcase);
464
+ return containerProps;
465
+ }
466
+ SchemaSync.enableForIModel = enableForIModel;
251
467
  /** Provides access to a cloud-based `SchemaSyncDb` to hold ECSchemas. */
252
468
  class CloudAccess extends CloudSqlite_1.CloudSqlite.DbAccess {
253
469
  constructor(props) {
@@ -264,6 +480,12 @@ var SchemaSync;
264
480
  static async initializeDb(props) {
265
481
  return super._initializeDb({ props, dbType: SchemaSyncDb, dbName: defaultDbName });
266
482
  }
483
+ /** Create and initialize a new `schema-sync` BlobContainer to hold a `SchemaSyncDb`. */
484
+ static async createNewContainer(args) {
485
+ const props = await this.createBlobContainer({ scope: args.scope, metadata: { ...args.metadata, containerType: SchemaSync.containerType } });
486
+ await this.initializeDb(props);
487
+ return props;
488
+ }
267
489
  }
268
490
  SchemaSync.CloudAccess = CloudAccess;
269
491
  })(SchemaSync || (exports.SchemaSync = SchemaSync = {}));