@sixb/sqlite 0.1.16 → 0.1.18
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/dist/ai-limit-storage.d.ts.map +1 -1
- package/dist/ai-usage-storage.d.ts.map +1 -1
- package/dist/execution-storage.d.ts +28 -0
- package/dist/execution-storage.d.ts.map +1 -1
- package/dist/file-download-grant-storage.d.ts +11 -0
- package/dist/file-download-grant-storage.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1362 -579
- package/dist/index.js.map +27 -25
- package/dist/migrations.d.ts.map +1 -1
- package/dist/objects/query-compiler.d.ts.map +1 -1
- package/dist/objects/reader.d.ts.map +1 -1
- package/dist/objects/storage.d.ts.map +1 -1
- package/dist/ontology-storage/index.d.ts +2 -0
- package/dist/ontology-storage/index.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-session.d.ts +17 -37
- package/dist/ontology-storage/materialization-session.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-state.d.ts +3 -26
- package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-writer.d.ts +1 -1
- package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
- package/dist/ontology-storage/materializations.d.ts +3 -6
- package/dist/ontology-storage/materializations.d.ts.map +1 -1
- package/dist/ontology-storage/outbox.d.ts.map +1 -1
- package/dist/ontology-storage/replacement-plans.d.ts +88 -0
- package/dist/ontology-storage/replacement-plans.d.ts.map +1 -0
- package/dist/ontology-storage/shared.d.ts +8 -5
- package/dist/ontology-storage/shared.d.ts.map +1 -1
- package/dist/ontology-storage/source-cleanup.d.ts.map +1 -1
- package/dist/ontology-storage/source-roots.d.ts +15 -2
- package/dist/ontology-storage/source-roots.d.ts.map +1 -1
- package/dist/ontology-storage/sources.d.ts +1 -2
- package/dist/ontology-storage/sources.d.ts.map +1 -1
- package/dist/projection-run-storage.d.ts.map +1 -1
- package/dist/rules-storage.d.ts.map +1 -1
- package/dist/testing.d.ts +4 -1
- package/dist/testing.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/ai-cost-storage.ts +10 -0
- package/src/ai-limit-storage.ts +13 -2
- package/src/ai-usage-storage.ts +9 -1
- package/src/execution-storage.ts +7 -2
- package/src/file-download-grant-storage.ts +76 -0
- package/src/index.ts +10 -3
- package/src/migrations/053-replacement-plans.sql +73 -0
- package/src/migrations/054-slim-ontology-outbox.sql +21 -0
- package/src/migrations/055-commit-touches.sql +31 -0
- package/src/migrations/056-audio-transcription.sql +10 -0
- package/src/migrations/057-reranking-model-kind.sql +16 -0
- package/src/migrations/058-file-download-grants.sql +21 -0
- package/src/migrations/059-drained-source-versions.sql +11 -0
- package/src/migrations.ts +115 -0
- package/src/objects/query-compiler.ts +5 -0
- package/src/objects/reader.ts +1 -0
- package/src/objects/storage.ts +5 -11
- package/src/ontology-storage/index.ts +6 -1
- package/src/ontology-storage/materialization-session.ts +120 -211
- package/src/ontology-storage/materialization-state.ts +5 -261
- package/src/ontology-storage/materialization-writer.ts +11 -13
- package/src/ontology-storage/materializations.ts +133 -210
- package/src/ontology-storage/outbox.ts +50 -16
- package/src/ontology-storage/replacement-plans.ts +1064 -0
- package/src/ontology-storage/shared.ts +17 -12
- package/src/ontology-storage/source-cleanup.ts +24 -9
- package/src/ontology-storage/source-roots.ts +38 -14
- package/src/ontology-storage/sources.ts +0 -26
- package/src/projection-run-storage.ts +7 -1
- package/src/rules-storage.ts +9 -14
- 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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
readonly
|
|
76
|
-
readonly
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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(
|
|
214
|
-
const rows = this.readLane(
|
|
215
|
-
if (rows.length === 0)
|
|
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
|
-
|
|
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
|
-
|
|
231
|
+
/** Staged object upserts and deletes of profiled types, or of objects with a stored vector. */
|
|
232
|
+
*vectorChangePages(
|
|
228
233
|
session: SqliteMaterializationSessionState,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
237
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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 (
|
|
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
|
-
|
|
399
|
-
|
|
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 ${
|
|
409
|
-
WHERE
|
|
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(
|
|
347
|
+
.all(session.workId, lane, limit) as WorkDatabaseRow[]
|
|
414
348
|
}
|
|
415
349
|
return this.db
|
|
416
350
|
.query(
|
|
417
|
-
`SELECT ${selected} FROM ${
|
|
418
|
-
WHERE
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
458
|
-
UNIQUE (
|
|
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
|
-
|
|
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
|
|
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
|