@sixb/sqlite 0.0.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/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/action-run-storage.d.ts +29 -0
- package/dist/action-run-storage.d.ts.map +1 -0
- package/dist/agents/agent-storage.d.ts +25 -0
- package/dist/agents/agent-storage.d.ts.map +1 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/messages.d.ts +14 -0
- package/dist/agents/messages.d.ts.map +1 -0
- package/dist/agents/rows.d.ts +77 -0
- package/dist/agents/rows.d.ts.map +1 -0
- package/dist/agents/runs.d.ts +26 -0
- package/dist/agents/runs.d.ts.map +1 -0
- package/dist/agents/threads.d.ts +14 -0
- package/dist/agents/threads.d.ts.map +1 -0
- package/dist/auth-storage/access-tokens.d.ts +33 -0
- package/dist/auth-storage/access-tokens.d.ts.map +1 -0
- package/dist/auth-storage/auth-storage.d.ts +46 -0
- package/dist/auth-storage/auth-storage.d.ts.map +1 -0
- package/dist/auth-storage/group-memberships.d.ts +21 -0
- package/dist/auth-storage/group-memberships.d.ts.map +1 -0
- package/dist/auth-storage/identities.d.ts +17 -0
- package/dist/auth-storage/identities.d.ts.map +1 -0
- package/dist/auth-storage/index.d.ts +3 -0
- package/dist/auth-storage/index.d.ts.map +1 -0
- package/dist/auth-storage/invitations.d.ts +28 -0
- package/dist/auth-storage/invitations.d.ts.map +1 -0
- package/dist/auth-storage/magic-links.d.ts +28 -0
- package/dist/auth-storage/magic-links.d.ts.map +1 -0
- package/dist/auth-storage/oidc-attempts.d.ts +18 -0
- package/dist/auth-storage/oidc-attempts.d.ts.map +1 -0
- package/dist/auth-storage/rows.d.ts +134 -0
- package/dist/auth-storage/rows.d.ts.map +1 -0
- package/dist/auth-storage/service-account-group-memberships.d.ts +17 -0
- package/dist/auth-storage/service-account-group-memberships.d.ts.map +1 -0
- package/dist/auth-storage/service-accounts.d.ts +14 -0
- package/dist/auth-storage/service-accounts.d.ts.map +1 -0
- package/dist/auth-storage/sessions.d.ts +55 -0
- package/dist/auth-storage/sessions.d.ts.map +1 -0
- package/dist/auth-storage/shared.d.ts +131 -0
- package/dist/auth-storage/shared.d.ts.map +1 -0
- package/dist/auth-storage/users.d.ts +19 -0
- package/dist/auth-storage/users.d.ts.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10204 -0
- package/dist/index.js.map +54 -0
- package/dist/latest-run-query.d.ts +22 -0
- package/dist/latest-run-query.d.ts.map +1 -0
- package/dist/migrations.d.ts +18 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/object-query-compiler.d.ts +44 -0
- package/dist/object-query-compiler.d.ts.map +1 -0
- package/dist/object-storage.d.ts +93 -0
- package/dist/object-storage.d.ts.map +1 -0
- package/dist/ontology-storage/commits.d.ts +14 -0
- package/dist/ontology-storage/commits.d.ts.map +1 -0
- package/dist/ontology-storage/index.d.ts +22 -0
- package/dist/ontology-storage/index.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-session.d.ts +49 -0
- package/dist/ontology-storage/materialization-session.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-state.d.ts +63 -0
- package/dist/ontology-storage/materialization-state.d.ts.map +1 -0
- package/dist/ontology-storage/materialization-writer.d.ts +15 -0
- package/dist/ontology-storage/materialization-writer.d.ts.map +1 -0
- package/dist/ontology-storage/materializations.d.ts +32 -0
- package/dist/ontology-storage/materializations.d.ts.map +1 -0
- package/dist/ontology-storage/outbox.d.ts +14 -0
- package/dist/ontology-storage/outbox.d.ts.map +1 -0
- package/dist/ontology-storage/shared.d.ts +91 -0
- package/dist/ontology-storage/shared.d.ts.map +1 -0
- package/dist/ontology-storage/sources.d.ts +26 -0
- package/dist/ontology-storage/sources.d.ts.map +1 -0
- package/dist/pipeline-run-storage.d.ts +26 -0
- package/dist/pipeline-run-storage.d.ts.map +1 -0
- package/dist/projection-run-storage.d.ts +32 -0
- package/dist/projection-run-storage.d.ts.map +1 -0
- package/dist/rules-storage.d.ts +33 -0
- package/dist/rules-storage.d.ts.map +1 -0
- package/dist/run-list-query.d.ts +27 -0
- package/dist/run-list-query.d.ts.map +1 -0
- package/dist/storage-errors.d.ts +2 -0
- package/dist/storage-errors.d.ts.map +1 -0
- package/dist/sync-run-storage.d.ts +23 -0
- package/dist/sync-run-storage.d.ts.map +1 -0
- package/dist/timeseries-storage.d.ts +41 -0
- package/dist/timeseries-storage.d.ts.map +1 -0
- package/dist/transactions.d.ts +15 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/tsconfig.json +5 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/webhook-delivery-storage.d.ts +27 -0
- package/dist/webhook-delivery-storage.d.ts.map +1 -0
- package/dist/webhook-run-storage.d.ts +22 -0
- package/dist/webhook-run-storage.d.ts.map +1 -0
- package/dist/workflow-intervention-storage.d.ts +26 -0
- package/dist/workflow-intervention-storage.d.ts.map +1 -0
- package/dist/workflow-run-storage.d.ts +63 -0
- package/dist/workflow-run-storage.d.ts.map +1 -0
- package/package.json +58 -0
- package/src/action-run-storage.ts +761 -0
- package/src/agents/agent-storage.ts +46 -0
- package/src/agents/index.ts +5 -0
- package/src/agents/messages.ts +162 -0
- package/src/agents/rows.ts +205 -0
- package/src/agents/runs.ts +389 -0
- package/src/agents/threads.ts +133 -0
- package/src/auth-storage/access-tokens.ts +289 -0
- package/src/auth-storage/auth-storage.ts +784 -0
- package/src/auth-storage/group-memberships.ts +52 -0
- package/src/auth-storage/identities.ts +98 -0
- package/src/auth-storage/index.ts +2 -0
- package/src/auth-storage/invitations.ts +258 -0
- package/src/auth-storage/magic-links.ts +149 -0
- package/src/auth-storage/oidc-attempts.ts +109 -0
- package/src/auth-storage/rows.ts +338 -0
- package/src/auth-storage/service-account-group-memberships.ts +161 -0
- package/src/auth-storage/service-accounts.ts +168 -0
- package/src/auth-storage/sessions.ts +230 -0
- package/src/auth-storage/shared.ts +707 -0
- package/src/auth-storage/users.ts +226 -0
- package/src/index.ts +292 -0
- package/src/latest-run-query.ts +49 -0
- package/src/migrations/001-initial-schema.sql +1068 -0
- package/src/migrations.ts +245 -0
- package/src/object-query-compiler.ts +1198 -0
- package/src/object-storage.ts +605 -0
- package/src/ontology-storage/commits.ts +127 -0
- package/src/ontology-storage/index.ts +35 -0
- package/src/ontology-storage/materialization-session.ts +393 -0
- package/src/ontology-storage/materialization-state.ts +901 -0
- package/src/ontology-storage/materialization-writer.ts +394 -0
- package/src/ontology-storage/materializations.ts +838 -0
- package/src/ontology-storage/outbox.ts +237 -0
- package/src/ontology-storage/shared.ts +269 -0
- package/src/ontology-storage/sources.ts +653 -0
- package/src/pipeline-run-storage.ts +506 -0
- package/src/projection-run-storage.ts +592 -0
- package/src/rules-storage.ts +375 -0
- package/src/run-list-query.ts +98 -0
- package/src/sql.d.ts +4 -0
- package/src/storage-errors.ts +3 -0
- package/src/sync-run-storage.ts +299 -0
- package/src/timeseries-storage.ts +212 -0
- package/src/transactions.ts +96 -0
- package/src/validation.ts +30 -0
- package/src/webhook-delivery-storage.ts +272 -0
- package/src/webhook-run-storage.ts +240 -0
- package/src/workflow-intervention-storage.ts +398 -0
- package/src/workflow-run-storage.ts +1148 -0
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite"
|
|
2
|
+
import type {
|
|
3
|
+
EffectiveLinkSnapshot,
|
|
4
|
+
EffectiveObjectSnapshot,
|
|
5
|
+
OntologyLinkRef,
|
|
6
|
+
OntologyObjectRef,
|
|
7
|
+
ProjectionEntityRef,
|
|
8
|
+
TelemetrySeriesRef,
|
|
9
|
+
} from "@sixb/core/internal/materialization"
|
|
10
|
+
import {
|
|
11
|
+
linkRefKey,
|
|
12
|
+
linkScopeSortKey,
|
|
13
|
+
MaterializationConflictError,
|
|
14
|
+
objectRefKey,
|
|
15
|
+
projectionEntityKey,
|
|
16
|
+
telemetryPointSortKey,
|
|
17
|
+
} from "@sixb/core/internal/materialization"
|
|
18
|
+
import {
|
|
19
|
+
appendScopeSnapshot,
|
|
20
|
+
finishScopeAccumulator,
|
|
21
|
+
startScopeAccumulator,
|
|
22
|
+
} from "@sixb/core/internal/ontology-storage-provider"
|
|
23
|
+
import type {
|
|
24
|
+
MaterializationLinkScopeState,
|
|
25
|
+
MaterializationLinkState,
|
|
26
|
+
MaterializationObjectState,
|
|
27
|
+
SourceReplacementLinkState,
|
|
28
|
+
SourceReplacementObjectState,
|
|
29
|
+
StoredLinkOverride,
|
|
30
|
+
StoredObjectOverride,
|
|
31
|
+
StoredSourceAssertion,
|
|
32
|
+
StoredSourceLinkAssertion,
|
|
33
|
+
StoredSourceObjectAssertion,
|
|
34
|
+
StoredTelemetryPoint,
|
|
35
|
+
} from "@sixb/core/storage"
|
|
36
|
+
import {
|
|
37
|
+
canonicalJson,
|
|
38
|
+
linkRefFromColumns,
|
|
39
|
+
objectRefFromColumns,
|
|
40
|
+
parseJson,
|
|
41
|
+
type SqliteOntologyOverrideRow,
|
|
42
|
+
type SqliteOntologySourceAssertionRow,
|
|
43
|
+
} from "./shared"
|
|
44
|
+
|
|
45
|
+
export const SQLITE_MATERIALIZATION_WORK_TABLE = "ontology_materialization_work"
|
|
46
|
+
export const SQLITE_REPLACEMENT_WORK_TABLE = "ontology_replacement_work"
|
|
47
|
+
|
|
48
|
+
interface EffectiveObjectRow {
|
|
49
|
+
readonly object_type_id: string
|
|
50
|
+
readonly primary_id: string
|
|
51
|
+
readonly properties: string
|
|
52
|
+
readonly created_at: string
|
|
53
|
+
readonly updated_at: string
|
|
54
|
+
readonly version: number
|
|
55
|
+
readonly last_commit_id: string | null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface EffectiveLinkRow {
|
|
59
|
+
readonly source_type_id: string
|
|
60
|
+
readonly source_id: string
|
|
61
|
+
readonly link_id: string
|
|
62
|
+
readonly target_type_id: string
|
|
63
|
+
readonly target_id: string
|
|
64
|
+
readonly properties: string | null
|
|
65
|
+
readonly created_at: string
|
|
66
|
+
readonly updated_at: string
|
|
67
|
+
readonly last_commit_id: string | null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface TelemetryRow {
|
|
71
|
+
readonly object_type_id: string
|
|
72
|
+
readonly object_id: string
|
|
73
|
+
readonly property_id: string
|
|
74
|
+
readonly value: string
|
|
75
|
+
readonly unit: string | null
|
|
76
|
+
readonly at: string
|
|
77
|
+
readonly last_commit_id: string | null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface LinkScopeRow extends EffectiveLinkRow {
|
|
81
|
+
readonly scope_sort_key: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface ObjectOverrideRow extends SqliteOntologyOverrideRow {
|
|
85
|
+
readonly object_type_id: string
|
|
86
|
+
readonly primary_id: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface LinkOverrideRow extends SqliteOntologyOverrideRow {
|
|
90
|
+
readonly source_type_id: string
|
|
91
|
+
readonly source_primary_id: string
|
|
92
|
+
readonly link_id: string
|
|
93
|
+
readonly target_type_id: string
|
|
94
|
+
readonly target_primary_id: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface ReplacementSources {
|
|
98
|
+
readonly owned: ReadonlySet<string>
|
|
99
|
+
readonly byMaterialization: ReadonlyMap<string, ReadonlyMap<string, StoredSourceAssertion>>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type ReplacementIdentity =
|
|
103
|
+
| {
|
|
104
|
+
readonly kind: "object"
|
|
105
|
+
readonly ref: OntologyObjectRef
|
|
106
|
+
readonly sortKey: string
|
|
107
|
+
readonly diffRequired: true
|
|
108
|
+
}
|
|
109
|
+
| {
|
|
110
|
+
readonly kind: "link"
|
|
111
|
+
readonly ref: OntologyLinkRef
|
|
112
|
+
readonly sortKey: string
|
|
113
|
+
readonly diffRequired: boolean
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface ReplacementIdentityInput {
|
|
117
|
+
readonly sessionId: string
|
|
118
|
+
readonly sourceId: string
|
|
119
|
+
readonly candidateMaterializationId: string
|
|
120
|
+
readonly previousMaterializationId: string | null
|
|
121
|
+
readonly kind: "object" | "link"
|
|
122
|
+
readonly pageRows: number
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export class SqliteMaterializationStateReader {
|
|
126
|
+
constructor(
|
|
127
|
+
private readonly db: Database,
|
|
128
|
+
readonly projectId: string
|
|
129
|
+
) {}
|
|
130
|
+
|
|
131
|
+
objectState(ref: OntologyObjectRef): MaterializationObjectState {
|
|
132
|
+
return this.objectStates([ref])[0]!
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
objectStates(refs: readonly OntologyObjectRef[]): readonly MaterializationObjectState[] {
|
|
136
|
+
if (refs.length === 0) return []
|
|
137
|
+
const requested = canonicalJson(
|
|
138
|
+
refs.map((ref) => ({ objectTypeId: ref.objectTypeId, primaryId: ref.primaryId }))
|
|
139
|
+
)
|
|
140
|
+
const requestedObjects = `
|
|
141
|
+
SELECT DISTINCT json_extract(value, '$.objectTypeId') AS object_type_id,
|
|
142
|
+
json_extract(value, '$.primaryId') AS primary_id
|
|
143
|
+
FROM json_each(?)
|
|
144
|
+
`
|
|
145
|
+
const effectiveRows = this.db
|
|
146
|
+
.query(
|
|
147
|
+
`WITH requested AS (${requestedObjects})
|
|
148
|
+
SELECT objects.* FROM objects
|
|
149
|
+
JOIN requested USING (object_type_id, primary_id)
|
|
150
|
+
WHERE objects.project_id = ?`
|
|
151
|
+
)
|
|
152
|
+
.all(requested, this.projectId) as EffectiveObjectRow[]
|
|
153
|
+
const sourceRows = this.db
|
|
154
|
+
.query(
|
|
155
|
+
`WITH requested AS (${requestedObjects})
|
|
156
|
+
SELECT rows.* FROM ontology_source_rows AS rows
|
|
157
|
+
JOIN requested USING (object_type_id, primary_id)
|
|
158
|
+
JOIN ontology_sources AS sources
|
|
159
|
+
ON sources.project_id = rows.project_id
|
|
160
|
+
AND sources.source_id = rows.source_id
|
|
161
|
+
AND sources.materialization_id = rows.materialization_id
|
|
162
|
+
WHERE rows.project_id = ? AND rows.entity_kind = 'object'
|
|
163
|
+
AND sources.status = 'active'`
|
|
164
|
+
)
|
|
165
|
+
.all(requested, this.projectId) as SqliteOntologySourceAssertionRow[]
|
|
166
|
+
const overrideRows = this.db
|
|
167
|
+
.query(
|
|
168
|
+
`WITH requested AS (${requestedObjects})
|
|
169
|
+
SELECT overrides.* FROM ontology_overrides AS overrides
|
|
170
|
+
JOIN requested USING (object_type_id, primary_id)
|
|
171
|
+
WHERE overrides.project_id = ? AND overrides.entity_kind = 'object'`
|
|
172
|
+
)
|
|
173
|
+
.all(requested, this.projectId) as ObjectOverrideRow[]
|
|
174
|
+
const telemetryRows = this.db
|
|
175
|
+
.query(
|
|
176
|
+
`WITH requested AS (${requestedObjects}), ranked AS (
|
|
177
|
+
SELECT timeseries.*,
|
|
178
|
+
ROW_NUMBER() OVER (
|
|
179
|
+
PARTITION BY timeseries.object_type_id, timeseries.object_id,
|
|
180
|
+
timeseries.property_id
|
|
181
|
+
ORDER BY timeseries.at DESC
|
|
182
|
+
) AS rank
|
|
183
|
+
FROM timeseries
|
|
184
|
+
JOIN requested
|
|
185
|
+
ON requested.object_type_id = timeseries.object_type_id
|
|
186
|
+
AND requested.primary_id = timeseries.object_id
|
|
187
|
+
WHERE timeseries.project_id = ?
|
|
188
|
+
)
|
|
189
|
+
SELECT * FROM ranked WHERE rank = 1`
|
|
190
|
+
)
|
|
191
|
+
.all(requested, this.projectId) as TelemetryRow[]
|
|
192
|
+
|
|
193
|
+
const effective = new Map(
|
|
194
|
+
effectiveRows.map((row) => [objectRefKey(objectRefFromColumns(row)), row] as const)
|
|
195
|
+
)
|
|
196
|
+
const sources = activeSourceMap(sourceRows)
|
|
197
|
+
const overrides = new Map(
|
|
198
|
+
overrideRows.map(
|
|
199
|
+
(row) => [objectRefKey(objectRefFromColumns(row)), storedObjectOverride(row)] as const
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
const telemetry = new Map<string, StoredTelemetryPoint[]>()
|
|
203
|
+
for (const row of telemetryRows) {
|
|
204
|
+
const key = objectRefKey({ objectTypeId: row.object_type_id, primaryId: row.object_id })
|
|
205
|
+
const points = telemetry.get(key) ?? []
|
|
206
|
+
points.push(storedPoint(row))
|
|
207
|
+
telemetry.set(key, points)
|
|
208
|
+
}
|
|
209
|
+
for (const points of telemetry.values()) {
|
|
210
|
+
points.sort((left, right) =>
|
|
211
|
+
telemetryPointSortKey(left.series, left.at).localeCompare(
|
|
212
|
+
telemetryPointSortKey(right.series, right.at)
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return refs.map((ref) => {
|
|
218
|
+
const key = objectRefKey(ref)
|
|
219
|
+
const effectiveRow = effective.get(key)
|
|
220
|
+
return {
|
|
221
|
+
ref: structuredClone(ref),
|
|
222
|
+
source: sourceObject(sources.get(projectionEntityKey({ kind: "object", ref }))),
|
|
223
|
+
override: overrides.get(key) ?? null,
|
|
224
|
+
effective: effectiveRow ? effectiveObjectSnapshot(effectiveRow) : null,
|
|
225
|
+
latestTelemetry: telemetry.get(key) ?? [],
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
linkState(ref: OntologyLinkRef): MaterializationLinkState {
|
|
231
|
+
return this.linkStates([ref])[0]!
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
linkStates(refs: readonly OntologyLinkRef[]): readonly MaterializationLinkState[] {
|
|
235
|
+
if (refs.length === 0) return []
|
|
236
|
+
const requested = canonicalJson(
|
|
237
|
+
refs.map((ref) => ({
|
|
238
|
+
sourceTypeId: ref.source.objectTypeId,
|
|
239
|
+
sourceId: ref.source.primaryId,
|
|
240
|
+
linkId: ref.linkId,
|
|
241
|
+
targetTypeId: ref.target.objectTypeId,
|
|
242
|
+
targetId: ref.target.primaryId,
|
|
243
|
+
}))
|
|
244
|
+
)
|
|
245
|
+
const effectiveRequest = `
|
|
246
|
+
SELECT DISTINCT json_extract(value, '$.sourceTypeId') AS source_type_id,
|
|
247
|
+
json_extract(value, '$.sourceId') AS source_id,
|
|
248
|
+
json_extract(value, '$.linkId') AS link_id,
|
|
249
|
+
json_extract(value, '$.targetTypeId') AS target_type_id,
|
|
250
|
+
json_extract(value, '$.targetId') AS target_id
|
|
251
|
+
FROM json_each(?)
|
|
252
|
+
`
|
|
253
|
+
const sourceRequest = `
|
|
254
|
+
SELECT DISTINCT json_extract(value, '$.sourceTypeId') AS source_type_id,
|
|
255
|
+
json_extract(value, '$.sourceId') AS source_primary_id,
|
|
256
|
+
json_extract(value, '$.linkId') AS link_id,
|
|
257
|
+
json_extract(value, '$.targetTypeId') AS target_type_id,
|
|
258
|
+
json_extract(value, '$.targetId') AS target_primary_id
|
|
259
|
+
FROM json_each(?)
|
|
260
|
+
`
|
|
261
|
+
const effectiveRows = this.db
|
|
262
|
+
.query(
|
|
263
|
+
`WITH requested AS (${effectiveRequest})
|
|
264
|
+
SELECT links.* FROM links
|
|
265
|
+
JOIN requested USING (source_type_id, source_id, link_id, target_type_id, target_id)
|
|
266
|
+
WHERE links.project_id = ?`
|
|
267
|
+
)
|
|
268
|
+
.all(requested, this.projectId) as EffectiveLinkRow[]
|
|
269
|
+
const sourceRows = this.db
|
|
270
|
+
.query(
|
|
271
|
+
`WITH requested AS (${sourceRequest})
|
|
272
|
+
SELECT rows.* FROM ontology_source_rows AS rows
|
|
273
|
+
JOIN requested USING (
|
|
274
|
+
source_type_id, source_primary_id, link_id, target_type_id, target_primary_id
|
|
275
|
+
)
|
|
276
|
+
JOIN ontology_sources AS sources
|
|
277
|
+
ON sources.project_id = rows.project_id
|
|
278
|
+
AND sources.source_id = rows.source_id
|
|
279
|
+
AND sources.materialization_id = rows.materialization_id
|
|
280
|
+
WHERE rows.project_id = ? AND rows.entity_kind = 'link'
|
|
281
|
+
AND sources.status = 'active'`
|
|
282
|
+
)
|
|
283
|
+
.all(requested, this.projectId) as SqliteOntologySourceAssertionRow[]
|
|
284
|
+
const overrideRows = this.db
|
|
285
|
+
.query(
|
|
286
|
+
`WITH requested AS (${sourceRequest})
|
|
287
|
+
SELECT overrides.* FROM ontology_overrides AS overrides
|
|
288
|
+
JOIN requested USING (
|
|
289
|
+
source_type_id, source_primary_id, link_id, target_type_id, target_primary_id
|
|
290
|
+
)
|
|
291
|
+
WHERE overrides.project_id = ? AND overrides.entity_kind = 'link'`
|
|
292
|
+
)
|
|
293
|
+
.all(requested, this.projectId) as LinkOverrideRow[]
|
|
294
|
+
const effective = new Map(
|
|
295
|
+
effectiveRows.map((row) => [linkRefKey(linkRefFromColumns(row)), row] as const)
|
|
296
|
+
)
|
|
297
|
+
const sources = activeSourceMap(sourceRows)
|
|
298
|
+
const overrides = new Map(
|
|
299
|
+
overrideRows.map(
|
|
300
|
+
(row) => [linkRefKey(linkRefFromOverrideColumns(row)), storedLinkOverride(row)] as const
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
return refs.map((ref) => {
|
|
304
|
+
const key = linkRefKey(ref)
|
|
305
|
+
const effectiveRow = effective.get(key)
|
|
306
|
+
return {
|
|
307
|
+
ref: structuredClone(ref),
|
|
308
|
+
source: sourceLink(sources.get(projectionEntityKey({ kind: "link", ref }))),
|
|
309
|
+
override: overrides.get(key) ?? null,
|
|
310
|
+
effective: effectiveRow ? effectiveLinkSnapshot(effectiveRow) : null,
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
exactPoint(series: TelemetrySeriesRef, at: string): StoredTelemetryPoint | null {
|
|
316
|
+
return this.exactPoints([{ series, at }])[0] ?? null
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
exactPoints(
|
|
320
|
+
points: readonly { readonly series: TelemetrySeriesRef; readonly at: string }[]
|
|
321
|
+
): readonly StoredTelemetryPoint[] {
|
|
322
|
+
if (points.length === 0) return []
|
|
323
|
+
const requested = canonicalJson(
|
|
324
|
+
points.map(({ series, at }) => ({
|
|
325
|
+
objectTypeId: series.object.objectTypeId,
|
|
326
|
+
objectId: series.object.primaryId,
|
|
327
|
+
propertyId: series.propertyId,
|
|
328
|
+
at,
|
|
329
|
+
}))
|
|
330
|
+
)
|
|
331
|
+
const rows = this.db
|
|
332
|
+
.query(
|
|
333
|
+
`WITH requested AS (
|
|
334
|
+
SELECT DISTINCT json_extract(value, '$.objectTypeId') AS object_type_id,
|
|
335
|
+
json_extract(value, '$.objectId') AS object_id,
|
|
336
|
+
json_extract(value, '$.propertyId') AS property_id,
|
|
337
|
+
json_extract(value, '$.at') AS at
|
|
338
|
+
FROM json_each(?)
|
|
339
|
+
)
|
|
340
|
+
SELECT timeseries.* FROM timeseries
|
|
341
|
+
JOIN requested USING (object_type_id, object_id, property_id, at)
|
|
342
|
+
WHERE timeseries.project_id = ?`
|
|
343
|
+
)
|
|
344
|
+
.all(requested, this.projectId) as TelemetryRow[]
|
|
345
|
+
const found = new Map(
|
|
346
|
+
rows.map((row) => {
|
|
347
|
+
const point = storedPoint(row)
|
|
348
|
+
return [telemetryPointSortKey(point.series, point.at), point] as const
|
|
349
|
+
})
|
|
350
|
+
)
|
|
351
|
+
return points.flatMap((point) => {
|
|
352
|
+
const stored = found.get(telemetryPointSortKey(point.series, point.at))
|
|
353
|
+
return stored ? [stored] : []
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
linkScopes(
|
|
358
|
+
scopes: readonly { readonly source: OntologyObjectRef; readonly linkId: string }[]
|
|
359
|
+
): readonly MaterializationLinkScopeState[] {
|
|
360
|
+
if (scopes.length === 0) return []
|
|
361
|
+
const requested = scopes.map(({ source, linkId }) => ({
|
|
362
|
+
scopeSortKey: linkScopeSortKey(source, linkId),
|
|
363
|
+
sourceTypeId: source.objectTypeId,
|
|
364
|
+
sourceId: source.primaryId,
|
|
365
|
+
linkId,
|
|
366
|
+
}))
|
|
367
|
+
const accumulators = new Map(
|
|
368
|
+
scopes.map(({ source, linkId }) => {
|
|
369
|
+
const key = linkScopeSortKey(source, linkId)
|
|
370
|
+
return [key, startScopeAccumulator(source, linkId, key)] as const
|
|
371
|
+
})
|
|
372
|
+
)
|
|
373
|
+
const rows = this.db
|
|
374
|
+
.query(
|
|
375
|
+
`
|
|
376
|
+
WITH requested AS (
|
|
377
|
+
SELECT DISTINCT
|
|
378
|
+
json_extract(value, '$.scopeSortKey') AS scope_sort_key,
|
|
379
|
+
json_extract(value, '$.sourceTypeId') AS source_type_id,
|
|
380
|
+
json_extract(value, '$.sourceId') AS source_id,
|
|
381
|
+
json_extract(value, '$.linkId') AS link_id
|
|
382
|
+
FROM json_each(?)
|
|
383
|
+
)
|
|
384
|
+
SELECT requested.scope_sort_key, links.*
|
|
385
|
+
FROM links
|
|
386
|
+
JOIN requested USING (source_type_id, source_id, link_id)
|
|
387
|
+
WHERE links.project_id = ?
|
|
388
|
+
ORDER BY requested.scope_sort_key, ${linkSortExpression("links")}
|
|
389
|
+
`
|
|
390
|
+
)
|
|
391
|
+
.iterate(canonicalJson(requested), this.projectId) as Iterable<LinkScopeRow>
|
|
392
|
+
for (const row of rows) {
|
|
393
|
+
const accumulator = accumulators.get(row.scope_sort_key)
|
|
394
|
+
if (!accumulator) {
|
|
395
|
+
throw new MaterializationConflictError(
|
|
396
|
+
"effective-state",
|
|
397
|
+
`Unexpected link scope '${row.scope_sort_key}' returned by storage.`
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
appendScopeSnapshot(accumulator, effectiveLinkSnapshot(row))
|
|
401
|
+
}
|
|
402
|
+
return scopes.map(({ source, linkId }) =>
|
|
403
|
+
finishScopeAccumulator(accumulators.get(linkScopeSortKey(source, linkId))!)
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
linkScope(source: OntologyObjectRef, linkId: string): MaterializationLinkScopeState {
|
|
408
|
+
const [scope] = this.linkScopes([{ source, linkId }])
|
|
409
|
+
if (!scope) {
|
|
410
|
+
throw new MaterializationConflictError(
|
|
411
|
+
"effective-state",
|
|
412
|
+
"Link scope lookup returned no row."
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
return scope
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
*incidentLinks(
|
|
419
|
+
objects: readonly OntologyObjectRef[],
|
|
420
|
+
pageRows: number
|
|
421
|
+
): Iterable<OntologyLinkRef[]> {
|
|
422
|
+
if (objects.length === 0) return
|
|
423
|
+
const values = objects.map(() => "(?, ?)").join(", ")
|
|
424
|
+
const args = objects.flatMap((ref) => [ref.objectTypeId, ref.primaryId])
|
|
425
|
+
let cursor: string | null = null
|
|
426
|
+
while (true) {
|
|
427
|
+
const rows = this.db
|
|
428
|
+
.query(
|
|
429
|
+
`
|
|
430
|
+
WITH requested(object_type_id, primary_id) AS (VALUES ${values}),
|
|
431
|
+
authority_links AS (
|
|
432
|
+
SELECT source_type_id, source_id, link_id, target_type_id, target_id
|
|
433
|
+
FROM links WHERE project_id = ?
|
|
434
|
+
UNION
|
|
435
|
+
SELECT source_type_id, source_primary_id, link_id, target_type_id, target_primary_id
|
|
436
|
+
FROM ontology_overrides
|
|
437
|
+
WHERE project_id = ? AND entity_kind = 'link'
|
|
438
|
+
UNION
|
|
439
|
+
SELECT rows.source_type_id, rows.source_primary_id, rows.link_id,
|
|
440
|
+
rows.target_type_id, rows.target_primary_id
|
|
441
|
+
FROM ontology_source_rows AS rows
|
|
442
|
+
JOIN ontology_sources AS sources
|
|
443
|
+
ON sources.project_id = rows.project_id
|
|
444
|
+
AND sources.source_id = rows.source_id
|
|
445
|
+
AND sources.materialization_id = rows.materialization_id
|
|
446
|
+
WHERE rows.project_id = ? AND rows.entity_kind = 'link' AND sources.status = 'active'
|
|
447
|
+
), selected AS (
|
|
448
|
+
SELECT DISTINCT links.*,
|
|
449
|
+
${linkSortExpression("links")} AS sort_key
|
|
450
|
+
FROM authority_links AS links
|
|
451
|
+
WHERE EXISTS (
|
|
452
|
+
SELECT 1 FROM requested
|
|
453
|
+
WHERE (requested.object_type_id = links.source_type_id
|
|
454
|
+
AND requested.primary_id = links.source_id)
|
|
455
|
+
OR (requested.object_type_id = links.target_type_id
|
|
456
|
+
AND requested.primary_id = links.target_id)
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
SELECT * FROM selected
|
|
460
|
+
WHERE (? IS NULL OR sort_key > ?)
|
|
461
|
+
ORDER BY sort_key
|
|
462
|
+
LIMIT ?
|
|
463
|
+
`
|
|
464
|
+
)
|
|
465
|
+
.all(
|
|
466
|
+
...args,
|
|
467
|
+
this.projectId,
|
|
468
|
+
this.projectId,
|
|
469
|
+
this.projectId,
|
|
470
|
+
cursor,
|
|
471
|
+
cursor,
|
|
472
|
+
pageRows
|
|
473
|
+
) as (EffectiveLinkRow & { readonly sort_key: string })[]
|
|
474
|
+
if (rows.length === 0) break
|
|
475
|
+
yield rows.map(linkRefFromColumns)
|
|
476
|
+
cursor = rows[rows.length - 1]?.sort_key ?? null
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
*replacementIdentities(input: ReplacementIdentityInput): Iterable<ReplacementIdentity[]> {
|
|
481
|
+
let cursor: string | null = null
|
|
482
|
+
while (true) {
|
|
483
|
+
if (input.kind === "object") {
|
|
484
|
+
const rows = this.replacementObjectRows(input, cursor)
|
|
485
|
+
if (rows.length === 0) break
|
|
486
|
+
yield rows.map((row) => ({
|
|
487
|
+
kind: "object",
|
|
488
|
+
ref: { objectTypeId: row.object_type_id, primaryId: row.primary_id },
|
|
489
|
+
sortKey: row.sort_key,
|
|
490
|
+
diffRequired: true,
|
|
491
|
+
}))
|
|
492
|
+
cursor = rows[rows.length - 1]?.sort_key ?? null
|
|
493
|
+
continue
|
|
494
|
+
}
|
|
495
|
+
const rows = this.replacementLinkRows(input, cursor)
|
|
496
|
+
if (rows.length === 0) break
|
|
497
|
+
yield rows.map((row) => ({
|
|
498
|
+
kind: "link",
|
|
499
|
+
ref: linkRefFromColumns(row),
|
|
500
|
+
sortKey: row.sort_key,
|
|
501
|
+
diffRequired: row.diff_required === 1,
|
|
502
|
+
}))
|
|
503
|
+
cursor = rows[rows.length - 1]?.sort_key ?? null
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
replacementObjectStates(
|
|
508
|
+
sourceId: string,
|
|
509
|
+
candidateMaterializationId: string,
|
|
510
|
+
refs: readonly OntologyObjectRef[]
|
|
511
|
+
): readonly SourceReplacementObjectState[] {
|
|
512
|
+
const base = this.objectStates(refs)
|
|
513
|
+
const candidates = this.replacementSources(
|
|
514
|
+
sourceId,
|
|
515
|
+
[candidateMaterializationId],
|
|
516
|
+
refs.map((ref) => ({ kind: "object" as const, ref }))
|
|
517
|
+
)
|
|
518
|
+
return base.map((state) => ({
|
|
519
|
+
ref: state.ref,
|
|
520
|
+
candidateSource: sourceObject(
|
|
521
|
+
candidates.byMaterialization
|
|
522
|
+
.get(candidateMaterializationId)
|
|
523
|
+
?.get(projectionEntityKey({ kind: "object", ref: state.ref }))
|
|
524
|
+
),
|
|
525
|
+
override: state.override,
|
|
526
|
+
effective: state.effective,
|
|
527
|
+
latestTelemetry: state.latestTelemetry,
|
|
528
|
+
}))
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
replacementLinkStates(
|
|
532
|
+
sourceId: string,
|
|
533
|
+
candidateMaterializationId: string,
|
|
534
|
+
materializationIds: readonly string[],
|
|
535
|
+
identities: readonly Extract<ReplacementIdentity, { readonly kind: "link" }>[]
|
|
536
|
+
): readonly SourceReplacementLinkState[] {
|
|
537
|
+
const refs = identities.map((identity) => identity.ref)
|
|
538
|
+
const base = this.linkStates(refs)
|
|
539
|
+
const replacements = this.replacementSources(
|
|
540
|
+
sourceId,
|
|
541
|
+
materializationIds,
|
|
542
|
+
refs.map((ref) => ({ kind: "link" as const, ref }))
|
|
543
|
+
)
|
|
544
|
+
const candidate = replacements.byMaterialization.get(candidateMaterializationId)
|
|
545
|
+
return base.map((state, index) => {
|
|
546
|
+
const key = projectionEntityKey({ kind: "link", ref: state.ref })
|
|
547
|
+
return {
|
|
548
|
+
ref: state.ref,
|
|
549
|
+
candidateSource: replacements.owned.has(key)
|
|
550
|
+
? sourceLink(candidate?.get(key))
|
|
551
|
+
: state.source,
|
|
552
|
+
override: state.override,
|
|
553
|
+
effective: state.effective,
|
|
554
|
+
diffRequired: identities[index]!.diffRequired,
|
|
555
|
+
}
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
effectiveObjectRevision(ref: OntologyObjectRef): {
|
|
560
|
+
readonly version: number
|
|
561
|
+
readonly lastCommitId: string | null
|
|
562
|
+
} | null {
|
|
563
|
+
const row = this.db
|
|
564
|
+
.query(
|
|
565
|
+
`SELECT version, last_commit_id FROM objects
|
|
566
|
+
WHERE project_id = ? AND object_type_id = ? AND primary_id = ?`
|
|
567
|
+
)
|
|
568
|
+
.get(this.projectId, ref.objectTypeId, ref.primaryId) as {
|
|
569
|
+
readonly version: number
|
|
570
|
+
readonly last_commit_id: string | null
|
|
571
|
+
} | null
|
|
572
|
+
return row ? { version: row.version, lastCommitId: row.last_commit_id } : null
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
effectiveLinkLastCommit(ref: OntologyLinkRef): string | null | undefined {
|
|
576
|
+
const row = this.db
|
|
577
|
+
.query(
|
|
578
|
+
`
|
|
579
|
+
SELECT last_commit_id FROM links
|
|
580
|
+
WHERE project_id = ? AND source_type_id = ? AND source_id = ? AND link_id = ?
|
|
581
|
+
AND target_type_id = ? AND target_id = ?
|
|
582
|
+
`
|
|
583
|
+
)
|
|
584
|
+
.get(
|
|
585
|
+
this.projectId,
|
|
586
|
+
ref.source.objectTypeId,
|
|
587
|
+
ref.source.primaryId,
|
|
588
|
+
ref.linkId,
|
|
589
|
+
ref.target.objectTypeId,
|
|
590
|
+
ref.target.primaryId
|
|
591
|
+
) as { readonly last_commit_id: string | null } | null
|
|
592
|
+
return row ? row.last_commit_id : undefined
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
overrideLastCommit(kind: "object" | "link", entityKey: string): string | null {
|
|
596
|
+
const row = this.db
|
|
597
|
+
.query(
|
|
598
|
+
`SELECT last_commit_id FROM ontology_overrides
|
|
599
|
+
WHERE project_id = ? AND entity_kind = ? AND entity_key = ?`
|
|
600
|
+
)
|
|
601
|
+
.get(this.projectId, kind, entityKey) as { readonly last_commit_id: string } | null
|
|
602
|
+
return row?.last_commit_id ?? null
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
private replacementSources(
|
|
606
|
+
sourceId: string,
|
|
607
|
+
materializationIds: readonly string[],
|
|
608
|
+
refs: readonly ProjectionEntityRef[]
|
|
609
|
+
): ReplacementSources {
|
|
610
|
+
if (materializationIds.length === 0 || refs.length === 0) {
|
|
611
|
+
return { owned: new Set(), byMaterialization: new Map() }
|
|
612
|
+
}
|
|
613
|
+
const requestedKeys = canonicalJson(refs.map((ref) => projectionEntityKey(ref)))
|
|
614
|
+
const requestedMaterializations = canonicalJson([...new Set(materializationIds)])
|
|
615
|
+
const rows = this.db
|
|
616
|
+
.query(
|
|
617
|
+
`WITH requested_entities AS (
|
|
618
|
+
SELECT value AS entity_key FROM json_each(?)
|
|
619
|
+
), requested_materializations AS (
|
|
620
|
+
SELECT value AS materialization_id FROM json_each(?)
|
|
621
|
+
)
|
|
622
|
+
SELECT rows.* FROM ontology_source_rows AS rows
|
|
623
|
+
JOIN requested_entities USING (entity_key)
|
|
624
|
+
JOIN requested_materializations USING (materialization_id)
|
|
625
|
+
WHERE rows.project_id = ? AND rows.source_id = ?`
|
|
626
|
+
)
|
|
627
|
+
.all(
|
|
628
|
+
requestedKeys,
|
|
629
|
+
requestedMaterializations,
|
|
630
|
+
this.projectId,
|
|
631
|
+
sourceId
|
|
632
|
+
) as SqliteOntologySourceAssertionRow[]
|
|
633
|
+
const owned = new Set<string>()
|
|
634
|
+
const byMaterialization = new Map<string, Map<string, StoredSourceAssertion>>()
|
|
635
|
+
for (const row of rows) {
|
|
636
|
+
const source = storedSource(row)
|
|
637
|
+
const key = projectionEntityKey(source.assertion)
|
|
638
|
+
owned.add(key)
|
|
639
|
+
const materialization = byMaterialization.get(row.materialization_id) ?? new Map()
|
|
640
|
+
materialization.set(key, source)
|
|
641
|
+
byMaterialization.set(row.materialization_id, materialization)
|
|
642
|
+
}
|
|
643
|
+
return { owned, byMaterialization }
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
private replacementObjectRows(
|
|
647
|
+
input: ReplacementIdentityInput,
|
|
648
|
+
cursor: string | null
|
|
649
|
+
): (EffectiveObjectRow & { readonly sort_key: string })[] {
|
|
650
|
+
return this.db
|
|
651
|
+
.query(
|
|
652
|
+
`
|
|
653
|
+
SELECT DISTINCT object_type_id, primary_id, entity_sort_key AS sort_key
|
|
654
|
+
FROM ontology_source_rows
|
|
655
|
+
WHERE project_id = ? AND source_id = ? AND entity_kind = 'object'
|
|
656
|
+
AND materialization_id IN (?, COALESCE(?, ''))
|
|
657
|
+
AND (? IS NULL OR entity_sort_key > ?)
|
|
658
|
+
ORDER BY sort_key
|
|
659
|
+
LIMIT ?
|
|
660
|
+
`
|
|
661
|
+
)
|
|
662
|
+
.all(
|
|
663
|
+
this.projectId,
|
|
664
|
+
input.sourceId,
|
|
665
|
+
input.candidateMaterializationId,
|
|
666
|
+
input.previousMaterializationId,
|
|
667
|
+
cursor,
|
|
668
|
+
cursor,
|
|
669
|
+
input.pageRows
|
|
670
|
+
) as (EffectiveObjectRow & { readonly sort_key: string })[]
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
private replacementLinkRows(
|
|
674
|
+
input: ReplacementIdentityInput,
|
|
675
|
+
cursor: string | null
|
|
676
|
+
): (EffectiveLinkRow & { readonly sort_key: string; readonly diff_required: number })[] {
|
|
677
|
+
return this.db
|
|
678
|
+
.query(
|
|
679
|
+
`
|
|
680
|
+
WITH incident_objects AS (
|
|
681
|
+
SELECT
|
|
682
|
+
json_extract(payload, '$.ref.objectTypeId') AS object_type_id,
|
|
683
|
+
json_extract(payload, '$.ref.primaryId') AS primary_id
|
|
684
|
+
FROM ${SQLITE_MATERIALIZATION_WORK_TABLE}
|
|
685
|
+
WHERE session_id = ? AND kind = 'incident-object'
|
|
686
|
+
), authority_links AS (
|
|
687
|
+
SELECT source_type_id, source_id, link_id, target_type_id, target_id
|
|
688
|
+
FROM links WHERE project_id = ?
|
|
689
|
+
UNION
|
|
690
|
+
SELECT source_type_id, source_primary_id, link_id, target_type_id, target_primary_id
|
|
691
|
+
FROM ontology_overrides
|
|
692
|
+
WHERE project_id = ? AND entity_kind = 'link'
|
|
693
|
+
UNION
|
|
694
|
+
SELECT rows.source_type_id, rows.source_primary_id, rows.link_id,
|
|
695
|
+
rows.target_type_id, rows.target_primary_id
|
|
696
|
+
FROM ontology_source_rows AS rows
|
|
697
|
+
JOIN ontology_sources AS sources
|
|
698
|
+
ON sources.project_id = rows.project_id
|
|
699
|
+
AND sources.source_id = rows.source_id
|
|
700
|
+
AND sources.materialization_id = rows.materialization_id
|
|
701
|
+
WHERE rows.project_id = ? AND rows.entity_kind = 'link' AND sources.status = 'active'
|
|
702
|
+
), replacement_links AS (
|
|
703
|
+
SELECT source_type_id, source_primary_id AS source_id, link_id,
|
|
704
|
+
target_type_id, target_primary_id AS target_id
|
|
705
|
+
FROM ontology_source_rows
|
|
706
|
+
WHERE project_id = ? AND source_id = ? AND entity_kind = 'link'
|
|
707
|
+
AND materialization_id IN (?, COALESCE(?, ''))
|
|
708
|
+
), incident_links AS (
|
|
709
|
+
SELECT links.* FROM authority_links AS links
|
|
710
|
+
WHERE EXISTS (
|
|
711
|
+
SELECT 1 FROM incident_objects
|
|
712
|
+
WHERE (incident_objects.object_type_id = links.source_type_id
|
|
713
|
+
AND incident_objects.primary_id = links.source_id)
|
|
714
|
+
OR (incident_objects.object_type_id = links.target_type_id
|
|
715
|
+
AND incident_objects.primary_id = links.target_id)
|
|
716
|
+
)
|
|
717
|
+
), diff_links AS (
|
|
718
|
+
SELECT * FROM replacement_links
|
|
719
|
+
UNION SELECT * FROM incident_links
|
|
720
|
+
), affected_scopes AS (
|
|
721
|
+
SELECT DISTINCT source_type_id, source_id, link_id FROM diff_links
|
|
722
|
+
), all_links AS (
|
|
723
|
+
SELECT *, 1 AS diff_required FROM diff_links
|
|
724
|
+
UNION ALL
|
|
725
|
+
SELECT links.source_type_id, links.source_id, links.link_id,
|
|
726
|
+
links.target_type_id, links.target_id, 0 AS diff_required
|
|
727
|
+
FROM links
|
|
728
|
+
JOIN affected_scopes USING (source_type_id, source_id, link_id)
|
|
729
|
+
WHERE links.project_id = ?
|
|
730
|
+
), selected AS (
|
|
731
|
+
SELECT source_type_id, source_id, link_id, target_type_id, target_id,
|
|
732
|
+
${linkSortExpression("all_links")} AS sort_key,
|
|
733
|
+
MAX(diff_required) AS diff_required
|
|
734
|
+
FROM all_links
|
|
735
|
+
GROUP BY source_type_id, source_id, link_id, target_type_id, target_id
|
|
736
|
+
)
|
|
737
|
+
SELECT * FROM selected
|
|
738
|
+
WHERE (? IS NULL OR sort_key > ?)
|
|
739
|
+
ORDER BY sort_key
|
|
740
|
+
LIMIT ?
|
|
741
|
+
`
|
|
742
|
+
)
|
|
743
|
+
.all(
|
|
744
|
+
input.sessionId,
|
|
745
|
+
this.projectId,
|
|
746
|
+
this.projectId,
|
|
747
|
+
this.projectId,
|
|
748
|
+
this.projectId,
|
|
749
|
+
input.sourceId,
|
|
750
|
+
input.candidateMaterializationId,
|
|
751
|
+
input.previousMaterializationId,
|
|
752
|
+
this.projectId,
|
|
753
|
+
cursor,
|
|
754
|
+
cursor,
|
|
755
|
+
input.pageRows
|
|
756
|
+
) as (EffectiveLinkRow & { readonly sort_key: string; readonly diff_required: number })[]
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function activeSourceMap(
|
|
761
|
+
rows: readonly SqliteOntologySourceAssertionRow[]
|
|
762
|
+
): ReadonlyMap<string, StoredSourceAssertion> {
|
|
763
|
+
const result = new Map<string, StoredSourceAssertion>()
|
|
764
|
+
for (const row of rows) {
|
|
765
|
+
const source = storedSource(row)
|
|
766
|
+
const key = projectionEntityKey(source.assertion)
|
|
767
|
+
if (result.has(key)) {
|
|
768
|
+
throw new MaterializationConflictError(
|
|
769
|
+
"source-materialization",
|
|
770
|
+
`Multiple active sources assert ${key}.`
|
|
771
|
+
)
|
|
772
|
+
}
|
|
773
|
+
result.set(key, source)
|
|
774
|
+
}
|
|
775
|
+
return result
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function sourceObject(
|
|
779
|
+
source: StoredSourceAssertion | undefined
|
|
780
|
+
): StoredSourceObjectAssertion | null {
|
|
781
|
+
return source?.assertion.kind === "object" ? (source as StoredSourceObjectAssertion) : null
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function sourceLink(source: StoredSourceAssertion | undefined): StoredSourceLinkAssertion | null {
|
|
785
|
+
return source?.assertion.kind === "link" ? (source as StoredSourceLinkAssertion) : null
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function storedObjectOverride(row: ObjectOverrideRow): StoredObjectOverride {
|
|
789
|
+
const ref = objectRefFromColumns(row)
|
|
790
|
+
return {
|
|
791
|
+
ref,
|
|
792
|
+
value: parseJson<StoredObjectOverride["value"]>(row.value),
|
|
793
|
+
lastCommitId: row.last_commit_id,
|
|
794
|
+
updatedAt: row.updated_at,
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function storedLinkOverride(row: LinkOverrideRow): StoredLinkOverride {
|
|
799
|
+
const ref = linkRefFromOverrideColumns(row)
|
|
800
|
+
return {
|
|
801
|
+
ref,
|
|
802
|
+
value: parseJson<StoredLinkOverride["value"]>(row.value),
|
|
803
|
+
lastCommitId: row.last_commit_id,
|
|
804
|
+
updatedAt: row.updated_at,
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function linkRefFromOverrideColumns(row: LinkOverrideRow): OntologyLinkRef {
|
|
809
|
+
return {
|
|
810
|
+
source: { objectTypeId: row.source_type_id, primaryId: row.source_primary_id },
|
|
811
|
+
linkId: row.link_id,
|
|
812
|
+
target: { objectTypeId: row.target_type_id, primaryId: row.target_primary_id },
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function storedSource(row: SqliteOntologySourceAssertionRow): StoredSourceAssertion {
|
|
817
|
+
return {
|
|
818
|
+
source: { projectionId: row.source_id },
|
|
819
|
+
materializationId: row.materialization_id,
|
|
820
|
+
root: parseJson<StoredSourceAssertion["root"]>(row.root),
|
|
821
|
+
assertion: parseJson<StoredSourceAssertion["assertion"]>(row.assertion),
|
|
822
|
+
stagingOrdinal: row.staging_ordinal,
|
|
823
|
+
} as StoredSourceAssertion
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function effectiveObjectSnapshot(row: EffectiveObjectRow): EffectiveObjectSnapshot {
|
|
827
|
+
if (!row.last_commit_id) {
|
|
828
|
+
throw new MaterializationConflictError(
|
|
829
|
+
"effective-state",
|
|
830
|
+
`Effective object ${row.object_type_id}:${row.primary_id} lacks materializer provenance.`
|
|
831
|
+
)
|
|
832
|
+
}
|
|
833
|
+
return {
|
|
834
|
+
ref: objectRefFromColumns(row),
|
|
835
|
+
properties: parseJson<EffectiveObjectSnapshot["properties"]>(row.properties),
|
|
836
|
+
version: row.version,
|
|
837
|
+
createdAt: row.created_at,
|
|
838
|
+
updatedAt: row.updated_at,
|
|
839
|
+
lastCommitId: row.last_commit_id,
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function effectiveLinkSnapshot(row: EffectiveLinkRow): EffectiveLinkSnapshot {
|
|
844
|
+
if (!row.last_commit_id) {
|
|
845
|
+
throw new MaterializationConflictError(
|
|
846
|
+
"effective-state",
|
|
847
|
+
`Effective link ${linkRefKey(linkRefFromColumns(row))} lacks materializer provenance.`
|
|
848
|
+
)
|
|
849
|
+
}
|
|
850
|
+
return {
|
|
851
|
+
ref: linkRefFromColumns(row),
|
|
852
|
+
...(row.properties === null
|
|
853
|
+
? {}
|
|
854
|
+
: {
|
|
855
|
+
properties: parseJson<NonNullable<EffectiveLinkSnapshot["properties"]>>(row.properties),
|
|
856
|
+
}),
|
|
857
|
+
createdAt: row.created_at,
|
|
858
|
+
updatedAt: row.updated_at,
|
|
859
|
+
lastCommitId: row.last_commit_id,
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function storedPoint(row: TelemetryRow): StoredTelemetryPoint {
|
|
864
|
+
if (!row.last_commit_id) {
|
|
865
|
+
throw new MaterializationConflictError(
|
|
866
|
+
"timeseries-point",
|
|
867
|
+
`Telemetry point ${telemetryPointSortKey(
|
|
868
|
+
{
|
|
869
|
+
object: { objectTypeId: row.object_type_id, primaryId: row.object_id },
|
|
870
|
+
propertyId: row.property_id,
|
|
871
|
+
},
|
|
872
|
+
row.at
|
|
873
|
+
)} lacks materializer provenance.`
|
|
874
|
+
)
|
|
875
|
+
}
|
|
876
|
+
return {
|
|
877
|
+
series: {
|
|
878
|
+
object: { objectTypeId: row.object_type_id, primaryId: row.object_id },
|
|
879
|
+
propertyId: row.property_id,
|
|
880
|
+
},
|
|
881
|
+
value: parseJson<StoredTelemetryPoint["value"]>(row.value),
|
|
882
|
+
...(row.unit === null ? {} : { unit: row.unit }),
|
|
883
|
+
at: row.at,
|
|
884
|
+
lastCommitId: row.last_commit_id,
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
export function objectSortExpression(alias?: string): string {
|
|
889
|
+
const prefix = alias ? `${alias}.` : ""
|
|
890
|
+
return `LOWER(HEX(CAST(json_array(${prefix}object_type_id, ${prefix}primary_id) AS BLOB)))`
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
export function linkSortExpression(alias?: string): string {
|
|
894
|
+
const prefix = alias ? `${alias}.` : ""
|
|
895
|
+
return `LOWER(HEX(CAST(json_array(${prefix}source_type_id, ${prefix}source_id, ${prefix}link_id, ${prefix}target_type_id, ${prefix}target_id) AS BLOB)))`
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
export function pointSortExpression(alias?: string): string {
|
|
899
|
+
const prefix = alias ? `${alias}.` : ""
|
|
900
|
+
return `LOWER(HEX(CAST(json_array(${prefix}object_type_id, ${prefix}object_id, ${prefix}property_id, ${prefix}at) AS BLOB)))`
|
|
901
|
+
}
|