@sixb/sqlite 0.1.16 → 0.1.17

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 (70) hide show
  1. package/dist/ai-limit-storage.d.ts.map +1 -1
  2. package/dist/ai-usage-storage.d.ts.map +1 -1
  3. package/dist/execution-storage.d.ts +28 -0
  4. package/dist/execution-storage.d.ts.map +1 -1
  5. package/dist/file-download-grant-storage.d.ts +11 -0
  6. package/dist/file-download-grant-storage.d.ts.map +1 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1362 -579
  10. package/dist/index.js.map +27 -25
  11. package/dist/migrations.d.ts.map +1 -1
  12. package/dist/objects/query-compiler.d.ts.map +1 -1
  13. package/dist/objects/reader.d.ts.map +1 -1
  14. package/dist/objects/storage.d.ts.map +1 -1
  15. package/dist/ontology-storage/index.d.ts +2 -0
  16. package/dist/ontology-storage/index.d.ts.map +1 -1
  17. package/dist/ontology-storage/materialization-session.d.ts +17 -37
  18. package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
  19. package/dist/ontology-storage/materialization-state.d.ts +3 -26
  20. package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
  21. package/dist/ontology-storage/materialization-writer.d.ts +1 -1
  22. package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
  23. package/dist/ontology-storage/materializations.d.ts +3 -6
  24. package/dist/ontology-storage/materializations.d.ts.map +1 -1
  25. package/dist/ontology-storage/outbox.d.ts.map +1 -1
  26. package/dist/ontology-storage/replacement-plans.d.ts +88 -0
  27. package/dist/ontology-storage/replacement-plans.d.ts.map +1 -0
  28. package/dist/ontology-storage/shared.d.ts +8 -5
  29. package/dist/ontology-storage/shared.d.ts.map +1 -1
  30. package/dist/ontology-storage/source-cleanup.d.ts.map +1 -1
  31. package/dist/ontology-storage/source-roots.d.ts +15 -2
  32. package/dist/ontology-storage/source-roots.d.ts.map +1 -1
  33. package/dist/ontology-storage/sources.d.ts +1 -2
  34. package/dist/ontology-storage/sources.d.ts.map +1 -1
  35. package/dist/projection-run-storage.d.ts.map +1 -1
  36. package/dist/rules-storage.d.ts.map +1 -1
  37. package/dist/testing.d.ts +4 -1
  38. package/dist/testing.d.ts.map +1 -1
  39. package/package.json +3 -3
  40. package/src/ai-cost-storage.ts +10 -0
  41. package/src/ai-limit-storage.ts +13 -2
  42. package/src/ai-usage-storage.ts +9 -1
  43. package/src/execution-storage.ts +7 -2
  44. package/src/file-download-grant-storage.ts +76 -0
  45. package/src/index.ts +10 -3
  46. package/src/migrations/053-replacement-plans.sql +73 -0
  47. package/src/migrations/054-slim-ontology-outbox.sql +21 -0
  48. package/src/migrations/055-commit-touches.sql +31 -0
  49. package/src/migrations/056-audio-transcription.sql +10 -0
  50. package/src/migrations/057-reranking-model-kind.sql +16 -0
  51. package/src/migrations/058-file-download-grants.sql +21 -0
  52. package/src/migrations/059-drained-source-versions.sql +11 -0
  53. package/src/migrations.ts +115 -0
  54. package/src/objects/query-compiler.ts +5 -0
  55. package/src/objects/reader.ts +1 -0
  56. package/src/objects/storage.ts +5 -11
  57. package/src/ontology-storage/index.ts +6 -1
  58. package/src/ontology-storage/materialization-session.ts +120 -211
  59. package/src/ontology-storage/materialization-state.ts +5 -261
  60. package/src/ontology-storage/materialization-writer.ts +11 -13
  61. package/src/ontology-storage/materializations.ts +133 -210
  62. package/src/ontology-storage/outbox.ts +50 -16
  63. package/src/ontology-storage/replacement-plans.ts +1064 -0
  64. package/src/ontology-storage/shared.ts +17 -12
  65. package/src/ontology-storage/source-cleanup.ts +24 -9
  66. package/src/ontology-storage/source-roots.ts +38 -14
  67. package/src/ontology-storage/sources.ts +0 -26
  68. package/src/projection-run-storage.ts +7 -1
  69. package/src/rules-storage.ts +9 -14
  70. package/src/testing.ts +6 -4
@@ -4,28 +4,28 @@ import {
4
4
  MaterializationConflictError,
5
5
  } from "@sixb/core/internal/materialization"
6
6
  import {
7
- correlateMaterializationChunk,
8
7
  duplicateMaterializationWork as duplicateWork,
9
- invalidCorrelation,
10
- materializationPlanItems,
11
8
  ProviderMaterializationSessionState,
12
9
  type ProviderMaterializationTransactionLifecycle,
13
10
  prepareMaterializationWork,
14
11
  } from "@sixb/core/internal/ontology-storage-provider"
15
12
  import type {
13
+ MaterializationCardinalityOccupantWorkRecord,
16
14
  MaterializationEventWorkRecord,
17
- MaterializationPlanChunk,
18
15
  MaterializationPlanHeader,
16
+ MaterializationPlanWorkRecord,
19
17
  MaterializationSession,
20
- MaterializationWorkPage,
18
+ MaterializationVectorChange,
21
19
  MaterializationWorkRecord,
22
20
  StageMaterializationWorkInput,
23
- StreamMaterializationWorkInput,
24
21
  } from "@sixb/core/storage"
22
+ import { SQLITE_MATERIALIZATION_WORK_TABLE } from "./materialization-state"
25
23
  import {
26
- SQLITE_MATERIALIZATION_WORK_TABLE,
27
- SQLITE_REPLACEMENT_WORK_TABLE,
28
- } from "./materialization-state"
24
+ assertReplacementPlanCoverage,
25
+ replacementPlanCounts,
26
+ SQLITE_PLAN_WORK_TABLE,
27
+ type SqliteReplacementPlan,
28
+ } from "./replacement-plans"
29
29
  import { canonicalJson, isSqliteConstraintError, parseJson } from "./shared"
30
30
 
31
31
  export interface SqliteOntologyTransactionContext {
@@ -52,9 +52,6 @@ interface WorkDatabaseRow {
52
52
  }
53
53
 
54
54
  interface WorkSummary {
55
- apply: number
56
- cardinality: number
57
- event: number
58
55
  objectClassifications: number
59
56
  linkClassifications: number
60
57
  pointClassifications: number
@@ -69,17 +66,29 @@ interface WorkSummary {
69
66
  latestObjectsChanged: number
70
67
  }
71
68
 
72
- export interface SqliteChunkSequenceProgress {
73
- readonly appliedPlanCount: number
74
- readonly appliedOutboxCount: number
75
- readonly appliedPlanCursor: WorkCursor | null
76
- readonly appliedEventCursor: WorkCursor | null
69
+ type WorkLane = "apply" | "cardinality" | "event"
70
+
71
+ interface LaneRecord {
72
+ readonly apply: MaterializationPlanWorkRecord
73
+ readonly cardinality: MaterializationCardinalityOccupantWorkRecord
74
+ readonly event: MaterializationEventWorkRecord
77
75
  }
78
76
 
79
77
  export class SqliteMaterializationSessionState extends ProviderMaterializationSessionState {
80
- appliedPlanCursor: WorkCursor | null = null
81
- appliedEventCursor: WorkCursor | null = null
82
78
  readonly summary: WorkSummary = emptyWorkSummary()
79
+ /** Work staged on the session lives in a temp table; a plan-bound session reads its plan's. */
80
+ readonly workTable: string
81
+ readonly workId: string | number
82
+
83
+ constructor(
84
+ header: MaterializationPlanHeader,
85
+ transactionId: object,
86
+ readonly plan: SqliteReplacementPlan | null
87
+ ) {
88
+ super(header, transactionId)
89
+ this.workTable = plan ? SQLITE_PLAN_WORK_TABLE : SQLITE_MATERIALIZATION_WORK_TABLE
90
+ this.workId = plan ? plan.versionId : this.id
91
+ }
83
92
  }
84
93
 
85
94
  export class SqliteMaterializationSessions {
@@ -91,7 +100,10 @@ export class SqliteMaterializationSessions {
91
100
  private readonly context: SqliteOntologyTransactionContext | null
92
101
  ) {}
93
102
 
94
- create(header: MaterializationPlanHeader): SqliteMaterializationSessionState {
103
+ create(
104
+ header: MaterializationPlanHeader,
105
+ plan: SqliteReplacementPlan | null
106
+ ): SqliteMaterializationSessionState {
95
107
  if (!this.context?.active) {
96
108
  throw new MaterializationConflictError(
97
109
  "effective-state",
@@ -99,7 +111,11 @@ export class SqliteMaterializationSessions {
99
111
  )
100
112
  }
101
113
  this.ensureTables()
102
- const session = new SqliteMaterializationSessionState(structuredClone(header), this.context.id)
114
+ const session = new SqliteMaterializationSessionState(
115
+ structuredClone(header),
116
+ this.context.id,
117
+ plan
118
+ )
103
119
  this.sessions.set(session.providerToken, session)
104
120
  this.live.add(session)
105
121
  this.context.materializations.register(session.providerToken)
@@ -125,13 +141,13 @@ export class SqliteMaterializationSessions {
125
141
  release(session: SqliteMaterializationSessionState): void {
126
142
  if (!session.active) return
127
143
  session.active = false
128
- session.replacement = null
129
144
  this.live.delete(session)
130
145
  // The normal transaction has one session. Dropping its transaction-local spool avoids an
131
146
  // indexed DELETE over hundreds of thousands of rows and releases temp pages immediately.
132
147
  // Preserve per-session deletion only when another live session still shares the tables.
148
+ // A plan's rows outlive the session: maintenance deletes them once the candidate moved on.
133
149
  if (this.live.size === 0) this.dropWorkTables()
134
- else this.deleteWork(session.id)
150
+ else if (!session.plan) this.deleteWork(session.id)
135
151
  this.context?.materializations.complete(session.providerToken)
136
152
  }
137
153
 
@@ -146,7 +162,7 @@ export class SqliteMaterializationSessions {
146
162
  const insert = this.db.query(
147
163
  `
148
164
  INSERT INTO ${SQLITE_MATERIALIZATION_WORK_TABLE} (
149
- session_id, record_key, unique_key, kind, lane,
165
+ work_id, record_key, unique_key, kind, lane,
150
166
  major_order, minor_order, sort_one, sort_two,
151
167
  classification_entity_kind, classification_identity_key,
152
168
  cardinality_view, cardinality_occupied, cardinality_source_type_id,
@@ -193,125 +209,87 @@ export class SqliteMaterializationSessions {
193
209
  }
194
210
  throw error
195
211
  }
196
- for (const prepared of records)
197
- recordSummary(session.summary, prepared.record, prepared.columns)
212
+ for (const prepared of records) recordSummary(session.summary, prepared.record)
198
213
  }
199
214
 
200
- async *stream(input: StreamMaterializationWorkInput): AsyncIterable<MaterializationWorkPage> {
201
- const session = this.require(input.session)
202
- const stream = session.workStreams[input.order]
203
- if (stream.started) {
204
- throw new MaterializationConflictError(
205
- "effective-state",
206
- `Materialization ${input.order} work may only be streamed once per session.`
207
- )
208
- }
209
- stream.started = true
210
- session.workSealed = true
215
+ /** Pages of one work lane in canonical order. */
216
+ *lanePages<TLane extends WorkLane>(
217
+ session: SqliteMaterializationSessionState,
218
+ lane: TLane,
219
+ pageRows: number
220
+ ): Iterable<readonly LaneRecord[TLane][]> {
211
221
  let cursor: WorkCursor | null = null
212
222
  while (true) {
213
- this.require(input.session)
214
- const rows = this.readLane(input.order, session.id, cursor, input.pageRows)
215
- if (rows.length === 0) break
216
- const records = rows.map((row) =>
217
- parseJson<MaterializationWorkRecord>(row.payload)
218
- ) as MaterializationWorkPage["records"]
223
+ this.require(session.publicSession())
224
+ const rows = this.readLane(session, lane, cursor, pageRows)
225
+ if (rows.length === 0) return
219
226
  cursor = workCursor(rows[rows.length - 1]!)
220
- stream.emittedCount += records.length
221
- yield { records }
227
+ yield rows.map((row) => parseJson<LaneRecord[TLane]>(row.payload))
222
228
  }
223
- this.require(input.session)
224
- stream.completed = true
225
229
  }
226
230
 
227
- readObjectExistence(
231
+ /** Staged object upserts and deletes of profiled types, or of objects with a stored vector. */
232
+ *vectorChangePages(
228
233
  session: SqliteMaterializationSessionState,
229
- refs: readonly { readonly objectTypeId: string; readonly primaryId: string }[]
230
- ): readonly {
231
- readonly ref: { readonly objectTypeId: string; readonly primaryId: string }
232
- readonly exists: boolean
233
- }[] {
234
- const lookup = this.db.query(
234
+ objectTypeIds: readonly string[],
235
+ pageRows: number
236
+ ): Iterable<readonly MaterializationVectorChange[]> {
237
+ if (
238
+ objectTypeIds.length === 0 &&
239
+ !this.db
240
+ .query(`SELECT 1 FROM object_vectors WHERE project_id = ? LIMIT 1`)
241
+ .get(session.header.commit.projectId)
242
+ ) {
243
+ return
244
+ }
245
+ const query = this.db.query(
235
246
  `
236
- SELECT payload FROM ${SQLITE_MATERIALIZATION_WORK_TABLE}
237
- WHERE session_id = ? AND unique_key = ? AND kind = 'object-existence'
247
+ WITH changes AS (
248
+ SELECT record_key, payload,
249
+ coalesce(
250
+ json_extract(payload, '$.item.value.row.ref.objectTypeId'),
251
+ json_extract(payload, '$.item.value.ref.objectTypeId')
252
+ ) AS object_type_id,
253
+ coalesce(
254
+ json_extract(payload, '$.item.value.row.ref.primaryId'),
255
+ json_extract(payload, '$.item.value.ref.primaryId')
256
+ ) AS primary_id
257
+ FROM ${session.workTable}
258
+ WHERE work_id = ? AND lane = 'apply' AND kind = 'plan'
259
+ AND json_extract(payload, '$.item.kind') IN ('object-upsert', 'object-delete')
260
+ AND record_key > ?
261
+ )
262
+ SELECT record_key, payload FROM changes
263
+ WHERE object_type_id IN (SELECT value FROM json_each(?))
264
+ OR EXISTS (
265
+ SELECT 1 FROM object_vectors
266
+ WHERE project_id = ? AND object_type_id = changes.object_type_id
267
+ AND primary_id = changes.primary_id
268
+ )
269
+ ORDER BY record_key
270
+ LIMIT ?
238
271
  `
239
272
  )
240
- return refs.flatMap((ref) => {
241
- const key = `object-existence:${JSON.stringify([ref.objectTypeId, ref.primaryId])}`
242
- const row = lookup.get(session.id, key) as { readonly payload: string } | null
243
- if (!row) return []
244
- const record = parseJson<
245
- Extract<MaterializationWorkRecord, { readonly kind: "object-existence" }>
246
- >(row.payload)
247
- return [{ ref: record.ref, exists: record.exists }]
248
- })
249
- }
250
-
251
- prepareChunkSequence(
252
- session: SqliteMaterializationSessionState,
253
- chunk: MaterializationPlanChunk
254
- ): SqliteChunkSequenceProgress {
255
- const items = materializationPlanItems(chunk)
256
- const planRows = this.readLane("apply", session.id, session.appliedPlanCursor, items.length)
257
- const expectedItems = planRows.map((row) => {
258
- const record = parseJson<Extract<MaterializationWorkRecord, { readonly kind: "plan" }>>(
259
- row.payload
260
- )
261
- return record.item
262
- })
263
- const eventRows = this.readLane(
264
- "event",
265
- session.id,
266
- session.appliedEventCursor,
267
- chunk.outbox.length
268
- )
269
- const expectedEvents = eventRows.map((row) =>
270
- parseJson<MaterializationEventWorkRecord>(row.payload)
271
- )
272
- const progress = correlateMaterializationChunk(
273
- session,
274
- chunk,
275
- items,
276
- expectedItems,
277
- expectedEvents
278
- )
279
- return {
280
- ...progress,
281
- appliedPlanCursor:
282
- planRows.length === 0
283
- ? session.appliedPlanCursor
284
- : workCursor(planRows[planRows.length - 1]!),
285
- appliedEventCursor:
286
- eventRows.length === 0
287
- ? session.appliedEventCursor
288
- : workCursor(eventRows[eventRows.length - 1]!),
289
- }
290
- }
291
-
292
- commitChunkSequence(
293
- session: SqliteMaterializationSessionState,
294
- progress: SqliteChunkSequenceProgress
295
- ): void {
296
- session.appliedPlanCount = progress.appliedPlanCount
297
- session.appliedOutboxCount = progress.appliedOutboxCount
298
- session.appliedPlanCursor = progress.appliedPlanCursor
299
- session.appliedEventCursor = progress.appliedEventCursor
300
- }
301
-
302
- laneCounts(session: SqliteMaterializationSessionState): {
303
- readonly apply: number
304
- readonly cardinality: number
305
- readonly event: number
306
- } {
307
- return {
308
- apply: session.summary.apply,
309
- cardinality: session.summary.cardinality,
310
- event: session.summary.event,
273
+ let after = ""
274
+ while (true) {
275
+ this.require(session.publicSession())
276
+ const rows = query.all(
277
+ session.workId,
278
+ after,
279
+ JSON.stringify(objectTypeIds),
280
+ session.header.commit.projectId,
281
+ pageRows
282
+ ) as { readonly record_key: string; readonly payload: string }[]
283
+ if (rows.length === 0) return
284
+ after = rows[rows.length - 1]!.record_key
285
+ yield rows.map(
286
+ (row) => parseJson<MaterializationPlanWorkRecord>(row.payload).item
287
+ ) as MaterializationVectorChange[]
311
288
  }
312
289
  }
313
290
 
314
291
  projectionCounts(session: SqliteMaterializationSessionState): EffectiveChangeCounts {
292
+ if (session.plan) return replacementPlanCounts(this.db, session.plan.versionId)
315
293
  const summary = session.summary
316
294
  return {
317
295
  objectsCreated: summary.objectsCreated,
@@ -347,56 +325,12 @@ export class SqliteMaterializationSessions {
347
325
  }
348
326
 
349
327
  assertClassificationCoverage(session: SqliteMaterializationSessionState): void {
350
- if (!session.replacement) return
351
- const expectedObject = session.replacement.projectionKind === "object" ? 1 : 0
352
- const mismatch = this.db
353
- .query(
354
- `
355
- WITH invalid AS (
356
- SELECT 1
357
- FROM ${SQLITE_REPLACEMENT_WORK_TABLE} AS expected
358
- WHERE expected.session_id = ? AND expected.diff_required = 1
359
- AND (expected.entity_kind = 'link' OR ? = 1)
360
- AND NOT EXISTS (
361
- SELECT 1 FROM ${SQLITE_MATERIALIZATION_WORK_TABLE} AS actual
362
- WHERE actual.session_id = expected.session_id
363
- AND actual.unique_key = 'classification:' || expected.entity_kind || ':'
364
- || expected.identity_key
365
- )
366
- UNION ALL
367
- SELECT 1
368
- FROM ${SQLITE_MATERIALIZATION_WORK_TABLE} AS actual
369
- WHERE actual.session_id = ?
370
- -- A bounded range uses the existing (session_id, unique_key) unique index and scans
371
- -- classification entries only, instead of sorting the complete JSON work spool.
372
- AND actual.unique_key >= 'classification:'
373
- AND actual.unique_key < 'classification;'
374
- AND (
375
- actual.classification_entity_kind = 'point'
376
- OR NOT EXISTS (
377
- SELECT 1 FROM ${SQLITE_REPLACEMENT_WORK_TABLE} AS expected
378
- WHERE expected.session_id = actual.session_id
379
- AND expected.entity_kind = actual.classification_entity_kind
380
- AND expected.identity_key = actual.classification_identity_key
381
- AND expected.diff_required = 1
382
- AND (expected.entity_kind = 'link' OR ? = 1)
383
- )
384
- )
385
- )
386
- SELECT 1 FROM invalid LIMIT 1
387
- `
388
- )
389
- .get(session.id, expectedObject, session.id, expectedObject)
390
- if (mismatch) {
391
- invalidCorrelation(
392
- "Projection replacement classification coverage does not match its streamed state."
393
- )
394
- }
328
+ if (session.plan) assertReplacementPlanCoverage(this.db, session.plan)
395
329
  }
396
330
 
397
331
  private readLane(
398
- lane: StreamMaterializationWorkInput["order"],
399
- sessionId: string,
332
+ session: SqliteMaterializationSessionState,
333
+ lane: WorkLane,
400
334
  cursor: WorkCursor | null,
401
335
  limit: number
402
336
  ): WorkDatabaseRow[] {
@@ -405,23 +339,23 @@ export class SqliteMaterializationSessions {
405
339
  if (!cursor) {
406
340
  return this.db
407
341
  .query(
408
- `SELECT ${selected} FROM ${SQLITE_MATERIALIZATION_WORK_TABLE}
409
- WHERE session_id = ? AND lane = ?
342
+ `SELECT ${selected} FROM ${session.workTable}
343
+ WHERE work_id = ? AND lane = ?
410
344
  ORDER BY major_order, minor_order, sort_one, sort_two, record_key
411
345
  LIMIT ?`
412
346
  )
413
- .all(sessionId, lane, limit) as WorkDatabaseRow[]
347
+ .all(session.workId, lane, limit) as WorkDatabaseRow[]
414
348
  }
415
349
  return this.db
416
350
  .query(
417
- `SELECT ${selected} FROM ${SQLITE_MATERIALIZATION_WORK_TABLE}
418
- WHERE session_id = ? AND lane = ?
351
+ `SELECT ${selected} FROM ${session.workTable}
352
+ WHERE work_id = ? AND lane = ?
419
353
  AND (major_order, minor_order, sort_one, sort_two, record_key) > (?, ?, ?, ?, ?)
420
354
  ORDER BY major_order, minor_order, sort_one, sort_two, record_key
421
355
  LIMIT ?`
422
356
  )
423
357
  .all(
424
- sessionId,
358
+ session.workId,
425
359
  lane,
426
360
  cursor.majorOrder,
427
361
  cursor.minorOrder,
@@ -435,7 +369,7 @@ export class SqliteMaterializationSessions {
435
369
  private ensureTables(): void {
436
370
  this.db.run(`
437
371
  CREATE TEMP TABLE IF NOT EXISTS ${SQLITE_MATERIALIZATION_WORK_TABLE} (
438
- session_id TEXT NOT NULL,
372
+ work_id TEXT NOT NULL,
439
373
  record_key TEXT NOT NULL,
440
374
  unique_key TEXT NOT NULL,
441
375
  kind TEXT NOT NULL,
@@ -454,41 +388,25 @@ export class SqliteMaterializationSessions {
454
388
  cardinality_target_type_id TEXT,
455
389
  cardinality_target_primary_id TEXT,
456
390
  payload TEXT NOT NULL CHECK (json_valid(payload)),
457
- PRIMARY KEY (session_id, record_key),
458
- UNIQUE (session_id, unique_key)
391
+ PRIMARY KEY (work_id, record_key),
392
+ UNIQUE (work_id, unique_key)
459
393
  );
460
394
  CREATE INDEX IF NOT EXISTS idx_ontology_materialization_work_lane
461
395
  ON ${SQLITE_MATERIALIZATION_WORK_TABLE}(
462
- session_id, lane, major_order, minor_order, sort_one, sort_two, record_key
463
- );
464
- CREATE TEMP TABLE IF NOT EXISTS ${SQLITE_REPLACEMENT_WORK_TABLE} (
465
- session_id TEXT NOT NULL,
466
- entity_kind TEXT NOT NULL,
467
- identity_key TEXT NOT NULL,
468
- sort_key TEXT NOT NULL,
469
- diff_required INTEGER NOT NULL CHECK (diff_required IN (0, 1)),
470
- PRIMARY KEY (session_id, entity_kind, identity_key)
471
- );
472
- CREATE INDEX IF NOT EXISTS idx_ontology_replacement_work_order
473
- ON ${SQLITE_REPLACEMENT_WORK_TABLE}(
474
- session_id, entity_kind, sort_key, identity_key
396
+ work_id, lane, major_order, minor_order, sort_one, sort_two, record_key
475
397
  );
476
398
  `)
477
399
  }
478
400
 
479
401
  private deleteWork(sessionId: string): void {
480
402
  this.db
481
- .query(`DELETE FROM ${SQLITE_MATERIALIZATION_WORK_TABLE} WHERE session_id = ?`)
482
- .run(sessionId)
483
- this.db
484
- .query(`DELETE FROM ${SQLITE_REPLACEMENT_WORK_TABLE} WHERE session_id = ?`)
403
+ .query(`DELETE FROM ${SQLITE_MATERIALIZATION_WORK_TABLE} WHERE work_id = ?`)
485
404
  .run(sessionId)
486
405
  }
487
406
 
488
407
  private dropWorkTables(): void {
489
408
  this.db.run(`
490
409
  DROP TABLE IF EXISTS ${SQLITE_MATERIALIZATION_WORK_TABLE};
491
- DROP TABLE IF EXISTS ${SQLITE_REPLACEMENT_WORK_TABLE};
492
410
  `)
493
411
  }
494
412
  }
@@ -505,9 +423,6 @@ function workCursor(row: WorkDatabaseRow): WorkCursor {
505
423
 
506
424
  function emptyWorkSummary(): WorkSummary {
507
425
  return {
508
- apply: 0,
509
- cardinality: 0,
510
- event: 0,
511
426
  objectClassifications: 0,
512
427
  linkClassifications: 0,
513
428
  pointClassifications: 0,
@@ -523,13 +438,7 @@ function emptyWorkSummary(): WorkSummary {
523
438
  }
524
439
  }
525
440
 
526
- function recordSummary(
527
- summary: WorkSummary,
528
- record: MaterializationWorkRecord,
529
- columns: { readonly lane: "none" | "apply" | "cardinality" | "event" }
530
- ): void {
531
- if (columns.lane !== "none") summary[columns.lane] += 1
532
-
441
+ function recordSummary(summary: WorkSummary, record: MaterializationWorkRecord): void {
533
442
  if (record.kind === "classification") {
534
443
  if (record.entityKind === "object") summary.objectClassifications += 1
535
444
  if (record.entityKind === "link") summary.linkClassifications += 1