@hraness/peopleblade 0.1.0

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.
@@ -0,0 +1,339 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ CREATE TABLE source_binding_incarnations (
4
+ id INTEGER PRIMARY KEY,
5
+ source_binding_id INTEGER NOT NULL REFERENCES source_bindings(id) ON DELETE RESTRICT,
6
+ authority TEXT NOT NULL CHECK(length(authority) BETWEEN 1 AND 64),
7
+ ordinal INTEGER NOT NULL CHECK(ordinal >= 1),
8
+ subject_sha256 TEXT NOT NULL CHECK(length(subject_sha256) = 64 AND subject_sha256 NOT GLOB '*[^0-9a-f]*'),
9
+ auth_sha256 TEXT NOT NULL CHECK(length(auth_sha256) = 64 AND auth_sha256 NOT GLOB '*[^0-9a-f]*'),
10
+ previous_incarnation_id INTEGER REFERENCES source_binding_incarnations(id) ON DELETE RESTRICT,
11
+ transition_kind TEXT NOT NULL CHECK(transition_kind IN ('initial','explicit-rebind')),
12
+ transition_evidence_sha256 TEXT CHECK(
13
+ transition_evidence_sha256 IS NULL OR (
14
+ length(transition_evidence_sha256) = 64
15
+ AND transition_evidence_sha256 NOT GLOB '*[^0-9a-f]*'
16
+ )
17
+ ),
18
+ transition_evidence_json TEXT CHECK(
19
+ transition_evidence_json IS NULL OR json_valid(transition_evidence_json)
20
+ ),
21
+ first_seen_at TEXT NOT NULL,
22
+ last_seen_at TEXT NOT NULL,
23
+ retired_at TEXT,
24
+ metadata_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(metadata_json)),
25
+ CHECK(
26
+ (transition_kind = 'initial' AND ordinal=1 AND previous_incarnation_id IS NULL
27
+ AND transition_evidence_sha256 IS NULL AND transition_evidence_json IS NULL)
28
+ OR
29
+ (transition_kind = 'explicit-rebind' AND ordinal>=2 AND previous_incarnation_id IS NOT NULL
30
+ AND transition_evidence_sha256 IS NOT NULL AND transition_evidence_json IS NOT NULL)
31
+ ),
32
+ UNIQUE(source_binding_id, ordinal)
33
+ ) STRICT;
34
+
35
+ CREATE UNIQUE INDEX source_binding_incarnations_current
36
+ ON source_binding_incarnations(source_binding_id) WHERE retired_at IS NULL;
37
+ CREATE INDEX source_binding_incarnations_history
38
+ ON source_binding_incarnations(source_binding_id, ordinal DESC);
39
+ CREATE INDEX source_binding_incarnations_subject_owner
40
+ ON source_binding_incarnations(authority, subject_sha256, source_binding_id);
41
+
42
+ CREATE TABLE source_binding_transition_guards (
43
+ source_binding_id INTEGER PRIMARY KEY REFERENCES source_bindings(id) ON DELETE RESTRICT,
44
+ retiring_incarnation_id INTEGER NOT NULL UNIQUE REFERENCES source_binding_incarnations(id) ON DELETE RESTRICT,
45
+ next_ordinal INTEGER NOT NULL CHECK(next_ordinal >= 2),
46
+ authority TEXT NOT NULL CHECK(length(authority) BETWEEN 1 AND 64),
47
+ next_subject_sha256 TEXT NOT NULL CHECK(length(next_subject_sha256) = 64 AND next_subject_sha256 NOT GLOB '*[^0-9a-f]*'),
48
+ next_auth_sha256 TEXT NOT NULL CHECK(length(next_auth_sha256) = 64 AND next_auth_sha256 NOT GLOB '*[^0-9a-f]*'),
49
+ transition_evidence_sha256 TEXT NOT NULL CHECK(
50
+ length(transition_evidence_sha256) = 64
51
+ AND transition_evidence_sha256 NOT GLOB '*[^0-9a-f]*'
52
+ ),
53
+ transition_evidence_json TEXT NOT NULL CHECK(json_valid(transition_evidence_json)),
54
+ observed_at TEXT NOT NULL CHECK(julianday(observed_at) IS NOT NULL),
55
+ expected_last_seen_at TEXT NOT NULL CHECK(julianday(expected_last_seen_at) IS NOT NULL),
56
+ expected_metadata_json TEXT NOT NULL CHECK(json_valid(expected_metadata_json)),
57
+ expected_complete_realm_inventory_observed_at TEXT,
58
+ expected_complete_realm_inventory_sha256 TEXT,
59
+ complete_realm_inventory_sha256 TEXT NOT NULL CHECK(
60
+ length(complete_realm_inventory_sha256) = 64
61
+ AND complete_realm_inventory_sha256 NOT GLOB '*[^0-9a-f]*'
62
+ ),
63
+ metadata_json TEXT NOT NULL CHECK(json_valid(metadata_json)),
64
+ CHECK(
65
+ (expected_complete_realm_inventory_observed_at IS NULL
66
+ AND expected_complete_realm_inventory_sha256 IS NULL)
67
+ OR
68
+ (julianday(expected_complete_realm_inventory_observed_at) IS NOT NULL
69
+ AND length(expected_complete_realm_inventory_sha256) = 64
70
+ AND expected_complete_realm_inventory_sha256 NOT GLOB '*[^0-9a-f]*')
71
+ )
72
+ ) STRICT;
73
+
74
+ INSERT INTO source_binding_incarnations(
75
+ source_binding_id,authority,ordinal,subject_sha256,auth_sha256,transition_kind,
76
+ first_seen_at,last_seen_at,metadata_json
77
+ )
78
+ SELECT id,authority,1,subject_sha256,auth_sha256,'initial',first_seen_at,last_seen_at,metadata_json
79
+ FROM source_bindings;
80
+
81
+ DROP TRIGGER source_bindings_identity_immutable;
82
+
83
+ CREATE TRIGGER source_bindings_stable_identity_immutable
84
+ BEFORE UPDATE OF authority,auth_id ON source_bindings FOR EACH ROW
85
+ BEGIN SELECT RAISE(ABORT, 'source binding authority and authorization ID are immutable'); END;
86
+
87
+ CREATE TRIGGER source_bindings_initial_incarnation
88
+ AFTER INSERT ON source_bindings FOR EACH ROW
89
+ BEGIN
90
+ INSERT INTO source_binding_incarnations(
91
+ source_binding_id,authority,ordinal,subject_sha256,auth_sha256,transition_kind,
92
+ first_seen_at,last_seen_at,metadata_json
93
+ ) VALUES (
94
+ NEW.id,NEW.authority,1,NEW.subject_sha256,NEW.auth_sha256,'initial',
95
+ NEW.first_seen_at,NEW.last_seen_at,NEW.metadata_json
96
+ );
97
+ END;
98
+
99
+ CREATE TRIGGER source_binding_incarnations_no_replace
100
+ BEFORE INSERT ON source_binding_incarnations FOR EACH ROW
101
+ WHEN EXISTS (
102
+ SELECT 1 FROM source_binding_incarnations
103
+ WHERE id=NEW.id
104
+ OR (source_binding_id=NEW.source_binding_id AND ordinal=NEW.ordinal)
105
+ )
106
+ BEGIN SELECT RAISE(ABORT, 'source binding incarnation history cannot be replaced'); END;
107
+
108
+ CREATE TRIGGER source_binding_incarnations_identity_immutable
109
+ BEFORE UPDATE ON source_binding_incarnations FOR EACH ROW
110
+ WHEN NEW.id IS NOT OLD.id
111
+ OR NEW.source_binding_id IS NOT OLD.source_binding_id
112
+ OR NEW.authority IS NOT OLD.authority
113
+ OR NEW.ordinal IS NOT OLD.ordinal
114
+ OR NEW.subject_sha256 IS NOT OLD.subject_sha256
115
+ OR NEW.auth_sha256 IS NOT OLD.auth_sha256
116
+ OR NEW.previous_incarnation_id IS NOT OLD.previous_incarnation_id
117
+ OR NEW.transition_kind IS NOT OLD.transition_kind
118
+ OR NEW.transition_evidence_sha256 IS NOT OLD.transition_evidence_sha256
119
+ OR NEW.transition_evidence_json IS NOT OLD.transition_evidence_json
120
+ OR NEW.first_seen_at IS NOT OLD.first_seen_at
121
+ BEGIN SELECT RAISE(ABORT, 'source binding incarnation identity is immutable'); END;
122
+
123
+ CREATE TRIGGER source_binding_incarnations_retired_state_immutable
124
+ BEFORE UPDATE OF last_seen_at,metadata_json ON source_binding_incarnations FOR EACH ROW
125
+ WHEN OLD.retired_at IS NOT NULL OR NEW.retired_at IS NOT NULL
126
+ BEGIN SELECT RAISE(ABORT, 'retired source binding incarnation state is immutable'); END;
127
+
128
+ CREATE TRIGGER source_binding_incarnations_current_state_shadow
129
+ BEFORE UPDATE OF last_seen_at,metadata_json ON source_binding_incarnations FOR EACH ROW
130
+ WHEN OLD.retired_at IS NULL AND NOT EXISTS (
131
+ SELECT 1 FROM source_bindings binding
132
+ WHERE binding.id=OLD.source_binding_id
133
+ AND binding.authority=OLD.authority
134
+ AND binding.last_seen_at=NEW.last_seen_at
135
+ AND binding.metadata_json=NEW.metadata_json
136
+ )
137
+ BEGIN SELECT RAISE(ABORT, 'current source binding incarnation state must shadow its binding'); END;
138
+
139
+ CREATE TRIGGER source_binding_incarnations_retirement_monotonic
140
+ BEFORE UPDATE OF retired_at ON source_binding_incarnations FOR EACH ROW
141
+ WHEN OLD.retired_at IS NOT NULL OR NEW.retired_at IS NULL
142
+ BEGIN SELECT RAISE(ABORT, 'source binding incarnation retirement is monotonic'); END;
143
+
144
+ CREATE TRIGGER source_binding_incarnations_retirement_authorized
145
+ BEFORE UPDATE OF retired_at ON source_binding_incarnations FOR EACH ROW
146
+ WHEN OLD.retired_at IS NULL AND NEW.retired_at IS NOT NULL AND NOT EXISTS (
147
+ SELECT 1 FROM source_binding_transition_guards guard
148
+ JOIN source_bindings binding ON binding.id=guard.source_binding_id
149
+ WHERE guard.source_binding_id=OLD.source_binding_id
150
+ AND guard.retiring_incarnation_id=OLD.id AND guard.next_ordinal=OLD.ordinal+1
151
+ AND guard.authority=OLD.authority
152
+ AND guard.observed_at=NEW.retired_at
153
+ AND julianday(guard.observed_at)>=julianday(OLD.last_seen_at)
154
+ AND guard.expected_last_seen_at=OLD.last_seen_at
155
+ AND guard.expected_metadata_json=OLD.metadata_json
156
+ AND binding.authority=OLD.authority
157
+ AND binding.subject_sha256=OLD.subject_sha256
158
+ AND binding.auth_sha256=OLD.auth_sha256
159
+ AND binding.last_seen_at=guard.expected_last_seen_at
160
+ AND binding.metadata_json=guard.expected_metadata_json
161
+ AND binding.complete_realm_inventory_observed_at
162
+ IS guard.expected_complete_realm_inventory_observed_at
163
+ AND binding.complete_realm_inventory_sha256
164
+ IS guard.expected_complete_realm_inventory_sha256
165
+ )
166
+ BEGIN SELECT RAISE(ABORT, 'current source binding incarnation requires an atomic successor'); END;
167
+
168
+ CREATE TRIGGER source_binding_transition_guards_snapshot_insert
169
+ BEFORE INSERT ON source_binding_transition_guards FOR EACH ROW
170
+ WHEN NOT EXISTS (
171
+ SELECT 1 FROM source_binding_incarnations incarnation
172
+ JOIN source_bindings binding ON binding.id=incarnation.source_binding_id
173
+ WHERE incarnation.id=NEW.retiring_incarnation_id
174
+ AND incarnation.source_binding_id=NEW.source_binding_id
175
+ AND incarnation.retired_at IS NULL
176
+ AND incarnation.ordinal+1=NEW.next_ordinal
177
+ AND incarnation.authority=NEW.authority
178
+ AND incarnation.last_seen_at=NEW.expected_last_seen_at
179
+ AND incarnation.metadata_json=NEW.expected_metadata_json
180
+ AND binding.authority=NEW.authority
181
+ AND binding.subject_sha256=incarnation.subject_sha256
182
+ AND binding.auth_sha256=incarnation.auth_sha256
183
+ AND binding.last_seen_at=NEW.expected_last_seen_at
184
+ AND binding.metadata_json=NEW.expected_metadata_json
185
+ AND binding.complete_realm_inventory_observed_at
186
+ IS NEW.expected_complete_realm_inventory_observed_at
187
+ AND binding.complete_realm_inventory_sha256
188
+ IS NEW.expected_complete_realm_inventory_sha256
189
+ )
190
+ BEGIN SELECT RAISE(ABORT, 'source binding transition guard does not match current reviewed state'); END;
191
+
192
+ CREATE TRIGGER source_binding_transition_guards_immutable
193
+ BEFORE UPDATE ON source_binding_transition_guards FOR EACH ROW
194
+ BEGIN SELECT RAISE(ABORT, 'source binding transition guard is immutable'); END;
195
+
196
+ CREATE TRIGGER source_binding_transition_guards_no_replace
197
+ BEFORE INSERT ON source_binding_transition_guards FOR EACH ROW
198
+ WHEN EXISTS (
199
+ SELECT 1 FROM source_binding_transition_guards
200
+ WHERE source_binding_id=NEW.source_binding_id
201
+ OR retiring_incarnation_id=NEW.retiring_incarnation_id
202
+ )
203
+ BEGIN SELECT RAISE(ABORT, 'source binding transition guard cannot be replaced'); END;
204
+
205
+ CREATE TRIGGER source_binding_transition_guards_consumed_delete
206
+ BEFORE DELETE ON source_binding_transition_guards FOR EACH ROW
207
+ WHEN NOT (
208
+ EXISTS (
209
+ SELECT 1 FROM source_binding_incarnations predecessor
210
+ WHERE predecessor.id=OLD.retiring_incarnation_id
211
+ AND predecessor.source_binding_id=OLD.source_binding_id
212
+ AND predecessor.authority=OLD.authority
213
+ AND predecessor.ordinal=OLD.next_ordinal-1
214
+ AND predecessor.retired_at=OLD.observed_at
215
+ AND predecessor.last_seen_at=OLD.expected_last_seen_at
216
+ AND predecessor.metadata_json=OLD.expected_metadata_json
217
+ )
218
+ AND EXISTS (
219
+ SELECT 1 FROM source_binding_incarnations successor
220
+ WHERE successor.source_binding_id=OLD.source_binding_id
221
+ AND successor.authority=OLD.authority
222
+ AND successor.ordinal=OLD.next_ordinal
223
+ AND successor.subject_sha256=OLD.next_subject_sha256
224
+ AND successor.auth_sha256=OLD.next_auth_sha256
225
+ AND successor.previous_incarnation_id=OLD.retiring_incarnation_id
226
+ AND successor.transition_kind='explicit-rebind'
227
+ AND successor.transition_evidence_sha256=OLD.transition_evidence_sha256
228
+ AND successor.transition_evidence_json=OLD.transition_evidence_json
229
+ AND successor.first_seen_at=OLD.observed_at
230
+ AND successor.last_seen_at=OLD.observed_at
231
+ AND successor.retired_at IS NULL
232
+ AND successor.metadata_json=OLD.metadata_json
233
+ )
234
+ AND EXISTS (
235
+ SELECT 1 FROM source_bindings binding
236
+ WHERE binding.id=OLD.source_binding_id
237
+ AND binding.authority=OLD.authority
238
+ AND binding.subject_sha256=OLD.next_subject_sha256
239
+ AND binding.auth_sha256=OLD.next_auth_sha256
240
+ AND binding.last_seen_at=OLD.observed_at
241
+ AND binding.complete_realm_inventory_observed_at=OLD.observed_at
242
+ AND binding.complete_realm_inventory_sha256=OLD.complete_realm_inventory_sha256
243
+ AND binding.metadata_json=OLD.metadata_json
244
+ )
245
+ )
246
+ BEGIN SELECT RAISE(ABORT, 'source binding transition guard can only be deleted after exact consumption'); END;
247
+
248
+ CREATE TRIGGER source_binding_incarnations_no_delete
249
+ BEFORE DELETE ON source_binding_incarnations FOR EACH ROW
250
+ BEGIN SELECT RAISE(ABORT, 'source binding incarnation history is immutable'); END;
251
+
252
+ CREATE TRIGGER source_binding_incarnations_subject_owner_insert
253
+ BEFORE INSERT ON source_binding_incarnations FOR EACH ROW
254
+ WHEN EXISTS (
255
+ SELECT 1 FROM source_binding_incarnations
256
+ WHERE authority=NEW.authority AND subject_sha256=NEW.subject_sha256
257
+ AND source_binding_id<>NEW.source_binding_id
258
+ )
259
+ BEGIN SELECT RAISE(ABORT, 'source binding subject belongs to another binding'); END;
260
+
261
+ CREATE TRIGGER source_binding_incarnations_chain_insert
262
+ BEFORE INSERT ON source_binding_incarnations FOR EACH ROW
263
+ WHEN NEW.transition_kind='explicit-rebind' AND NOT EXISTS (
264
+ SELECT 1 FROM source_binding_incarnations previous
265
+ WHERE previous.id=NEW.previous_incarnation_id
266
+ AND previous.source_binding_id=NEW.source_binding_id
267
+ AND previous.authority=NEW.authority
268
+ AND previous.ordinal=NEW.ordinal-1 AND previous.retired_at IS NOT NULL
269
+ AND (previous.subject_sha256<>NEW.subject_sha256 OR previous.auth_sha256<>NEW.auth_sha256)
270
+ AND EXISTS (
271
+ SELECT 1 FROM source_bindings binding
272
+ WHERE binding.id=NEW.source_binding_id AND binding.authority=NEW.authority
273
+ )
274
+ )
275
+ BEGIN SELECT RAISE(ABORT, 'source binding incarnation predecessor is invalid'); END;
276
+
277
+ CREATE TRIGGER source_binding_incarnations_atomic_successor
278
+ AFTER UPDATE OF retired_at ON source_binding_incarnations FOR EACH ROW
279
+ WHEN OLD.retired_at IS NULL AND NEW.retired_at IS NOT NULL
280
+ BEGIN
281
+ INSERT INTO source_binding_incarnations(
282
+ source_binding_id,authority,ordinal,subject_sha256,auth_sha256,previous_incarnation_id,
283
+ transition_kind,transition_evidence_sha256,transition_evidence_json,
284
+ first_seen_at,last_seen_at,metadata_json
285
+ )
286
+ SELECT source_binding_id,authority,next_ordinal,next_subject_sha256,next_auth_sha256,
287
+ retiring_incarnation_id,'explicit-rebind',transition_evidence_sha256,
288
+ transition_evidence_json,observed_at,observed_at,metadata_json
289
+ FROM source_binding_transition_guards
290
+ WHERE source_binding_id=NEW.source_binding_id
291
+ AND retiring_incarnation_id=NEW.id AND next_ordinal=NEW.ordinal+1;
292
+
293
+ UPDATE source_bindings
294
+ SET subject_sha256=(SELECT next_subject_sha256 FROM source_binding_transition_guards
295
+ WHERE source_binding_id=NEW.source_binding_id),
296
+ auth_sha256=(SELECT next_auth_sha256 FROM source_binding_transition_guards
297
+ WHERE source_binding_id=NEW.source_binding_id),
298
+ last_seen_at=(SELECT observed_at FROM source_binding_transition_guards
299
+ WHERE source_binding_id=NEW.source_binding_id),
300
+ complete_realm_inventory_observed_at=(SELECT observed_at FROM source_binding_transition_guards
301
+ WHERE source_binding_id=NEW.source_binding_id),
302
+ complete_realm_inventory_sha256=(SELECT complete_realm_inventory_sha256
303
+ FROM source_binding_transition_guards WHERE source_binding_id=NEW.source_binding_id),
304
+ metadata_json=(SELECT metadata_json FROM source_binding_transition_guards
305
+ WHERE source_binding_id=NEW.source_binding_id)
306
+ WHERE id=NEW.source_binding_id;
307
+
308
+ DELETE FROM source_binding_transition_guards WHERE source_binding_id=NEW.source_binding_id;
309
+ END;
310
+
311
+ CREATE TRIGGER source_bindings_identity_shadow_guard
312
+ BEFORE UPDATE OF subject_sha256,auth_sha256 ON source_bindings FOR EACH ROW
313
+ WHEN NOT EXISTS (
314
+ SELECT 1 FROM source_binding_incarnations
315
+ WHERE source_binding_id=NEW.id AND retired_at IS NULL
316
+ AND authority=NEW.authority
317
+ AND subject_sha256=NEW.subject_sha256 AND auth_sha256=NEW.auth_sha256
318
+ )
319
+ BEGIN SELECT RAISE(ABORT, 'source binding identity must shadow its current incarnation'); END;
320
+
321
+ CREATE TRIGGER source_bindings_current_incarnation_metadata
322
+ AFTER UPDATE OF last_seen_at,metadata_json ON source_bindings FOR EACH ROW
323
+ BEGIN
324
+ UPDATE source_binding_incarnations
325
+ SET last_seen_at=NEW.last_seen_at,metadata_json=NEW.metadata_json
326
+ WHERE source_binding_id=NEW.id AND retired_at IS NULL;
327
+ END;
328
+
329
+ CREATE VIEW current_source_binding_incarnations AS
330
+ SELECT incarnation.id,incarnation.source_binding_id,incarnation.authority,
331
+ incarnation.ordinal,incarnation.subject_sha256,incarnation.auth_sha256,
332
+ incarnation.first_seen_at,incarnation.last_seen_at,incarnation.metadata_json
333
+ FROM source_binding_incarnations incarnation
334
+ JOIN source_bindings binding
335
+ ON binding.id=incarnation.source_binding_id
336
+ AND binding.authority=incarnation.authority
337
+ AND binding.subject_sha256=incarnation.subject_sha256
338
+ AND binding.auth_sha256=incarnation.auth_sha256
339
+ WHERE incarnation.retired_at IS NULL;
@@ -0,0 +1,121 @@
1
+ PRAGMA foreign_keys = ON;
2
+
3
+ DROP TRIGGER source_binding_transition_guards_snapshot_insert;
4
+
5
+ CREATE TRIGGER source_binding_transition_guards_snapshot_insert
6
+ BEFORE INSERT ON source_binding_transition_guards FOR EACH ROW
7
+ WHEN NOT EXISTS (
8
+ SELECT 1 FROM source_binding_incarnations incarnation
9
+ JOIN source_bindings binding ON binding.id=incarnation.source_binding_id
10
+ WHERE incarnation.id=NEW.retiring_incarnation_id
11
+ AND incarnation.source_binding_id=NEW.source_binding_id
12
+ AND incarnation.retired_at IS NULL
13
+ AND incarnation.ordinal+1=NEW.next_ordinal
14
+ AND incarnation.authority=NEW.authority
15
+ AND (incarnation.subject_sha256<>NEW.next_subject_sha256
16
+ OR incarnation.auth_sha256<>NEW.next_auth_sha256)
17
+ AND julianday(NEW.observed_at)>=julianday(incarnation.last_seen_at)
18
+ AND incarnation.last_seen_at=NEW.expected_last_seen_at
19
+ AND incarnation.metadata_json=NEW.expected_metadata_json
20
+ AND binding.authority=NEW.authority
21
+ AND binding.subject_sha256=incarnation.subject_sha256
22
+ AND binding.auth_sha256=incarnation.auth_sha256
23
+ AND binding.last_seen_at=NEW.expected_last_seen_at
24
+ AND binding.metadata_json=NEW.expected_metadata_json
25
+ AND binding.complete_realm_inventory_observed_at
26
+ IS NEW.expected_complete_realm_inventory_observed_at
27
+ AND binding.complete_realm_inventory_sha256
28
+ IS NEW.expected_complete_realm_inventory_sha256
29
+ AND NOT EXISTS (
30
+ SELECT 1 FROM source_binding_incarnations owner
31
+ WHERE owner.authority=NEW.authority
32
+ AND owner.subject_sha256=NEW.next_subject_sha256
33
+ AND owner.source_binding_id<>NEW.source_binding_id
34
+ )
35
+ )
36
+ BEGIN SELECT RAISE(ABORT, 'source binding transition guard does not match current reviewed state'); END;
37
+
38
+ DROP TRIGGER source_binding_transition_guards_consumed_delete;
39
+
40
+ CREATE TRIGGER source_binding_transition_guards_consumed_delete
41
+ BEFORE DELETE ON source_binding_transition_guards FOR EACH ROW
42
+ WHEN NOT (
43
+ (
44
+ EXISTS (
45
+ SELECT 1 FROM source_binding_incarnations predecessor
46
+ WHERE predecessor.id=OLD.retiring_incarnation_id
47
+ AND predecessor.source_binding_id=OLD.source_binding_id
48
+ AND predecessor.authority=OLD.authority
49
+ AND predecessor.ordinal=OLD.next_ordinal-1
50
+ AND predecessor.retired_at=OLD.observed_at
51
+ AND predecessor.last_seen_at=OLD.expected_last_seen_at
52
+ AND predecessor.metadata_json=OLD.expected_metadata_json
53
+ )
54
+ AND EXISTS (
55
+ SELECT 1 FROM source_binding_incarnations successor
56
+ WHERE successor.source_binding_id=OLD.source_binding_id
57
+ AND successor.authority=OLD.authority
58
+ AND successor.ordinal=OLD.next_ordinal
59
+ AND successor.subject_sha256=OLD.next_subject_sha256
60
+ AND successor.auth_sha256=OLD.next_auth_sha256
61
+ AND successor.previous_incarnation_id=OLD.retiring_incarnation_id
62
+ AND successor.transition_kind='explicit-rebind'
63
+ AND successor.transition_evidence_sha256=OLD.transition_evidence_sha256
64
+ AND successor.transition_evidence_json=OLD.transition_evidence_json
65
+ AND successor.first_seen_at=OLD.observed_at
66
+ AND successor.last_seen_at=OLD.observed_at
67
+ AND successor.retired_at IS NULL
68
+ AND successor.metadata_json=OLD.metadata_json
69
+ )
70
+ AND EXISTS (
71
+ SELECT 1 FROM source_bindings binding
72
+ WHERE binding.id=OLD.source_binding_id
73
+ AND binding.authority=OLD.authority
74
+ AND binding.subject_sha256=OLD.next_subject_sha256
75
+ AND binding.auth_sha256=OLD.next_auth_sha256
76
+ AND binding.last_seen_at=OLD.observed_at
77
+ AND binding.complete_realm_inventory_observed_at=OLD.observed_at
78
+ AND binding.complete_realm_inventory_sha256=OLD.complete_realm_inventory_sha256
79
+ AND binding.metadata_json=OLD.metadata_json
80
+ )
81
+ )
82
+ OR (
83
+ NOT EXISTS (
84
+ SELECT 1 FROM source_binding_incarnations successor
85
+ WHERE successor.source_binding_id=OLD.source_binding_id
86
+ AND successor.ordinal=OLD.next_ordinal
87
+ )
88
+ AND EXISTS (
89
+ SELECT 1 FROM source_binding_incarnations predecessor
90
+ JOIN source_bindings binding ON binding.id=predecessor.source_binding_id
91
+ WHERE predecessor.id=OLD.retiring_incarnation_id
92
+ AND predecessor.source_binding_id=OLD.source_binding_id
93
+ AND predecessor.authority=OLD.authority
94
+ AND predecessor.ordinal=OLD.next_ordinal-1
95
+ AND predecessor.retired_at IS NULL
96
+ AND binding.authority=OLD.authority
97
+ AND binding.subject_sha256=predecessor.subject_sha256
98
+ AND binding.auth_sha256=predecessor.auth_sha256
99
+ AND (
100
+ (predecessor.subject_sha256=OLD.next_subject_sha256
101
+ AND predecessor.auth_sha256=OLD.next_auth_sha256)
102
+ OR julianday(OLD.observed_at)<julianday(predecessor.last_seen_at)
103
+ OR predecessor.last_seen_at IS NOT OLD.expected_last_seen_at
104
+ OR predecessor.metadata_json IS NOT OLD.expected_metadata_json
105
+ OR binding.last_seen_at IS NOT OLD.expected_last_seen_at
106
+ OR binding.metadata_json IS NOT OLD.expected_metadata_json
107
+ OR binding.complete_realm_inventory_observed_at
108
+ IS NOT OLD.expected_complete_realm_inventory_observed_at
109
+ OR binding.complete_realm_inventory_sha256
110
+ IS NOT OLD.expected_complete_realm_inventory_sha256
111
+ OR EXISTS (
112
+ SELECT 1 FROM source_binding_incarnations owner
113
+ WHERE owner.authority=OLD.authority
114
+ AND owner.subject_sha256=OLD.next_subject_sha256
115
+ AND owner.source_binding_id<>OLD.source_binding_id
116
+ )
117
+ )
118
+ )
119
+ )
120
+ )
121
+ BEGIN SELECT RAISE(ABORT, 'source binding transition guard can only be deleted after exact consumption or proven invalidation'); END;