nl2data-semantic-catalog-postgres 0.1.0__py3-none-any.whl
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.
- nl2data_semantic_catalog_postgres/__init__.py +18 -0
- nl2data_semantic_catalog_postgres/client.py +139 -0
- nl2data_semantic_catalog_postgres/config.py +136 -0
- nl2data_semantic_catalog_postgres/envelope.py +327 -0
- nl2data_semantic_catalog_postgres/errors.py +188 -0
- nl2data_semantic_catalog_postgres/fake_postgres/__init__.py +53 -0
- nl2data_semantic_catalog_postgres/fake_postgres/driver.py +209 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_audit.py +243 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_drafts.py +84 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_maintenance.py +263 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_publications.py +249 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_schema.py +23 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_snapshots.py +197 -0
- nl2data_semantic_catalog_postgres/fake_postgres/handlers_versions.py +330 -0
- nl2data_semantic_catalog_postgres/fake_postgres/keys.py +99 -0
- nl2data_semantic_catalog_postgres/fake_postgres/pool.py +152 -0
- nl2data_semantic_catalog_postgres/fake_postgres/registry.py +144 -0
- nl2data_semantic_catalog_postgres/maintenance.py +235 -0
- nl2data_semantic_catalog_postgres/py.typed +0 -0
- nl2data_semantic_catalog_postgres/repositories/__init__.py +26 -0
- nl2data_semantic_catalog_postgres/repositories/activation.py +849 -0
- nl2data_semantic_catalog_postgres/repositories/audit_evidence.py +221 -0
- nl2data_semantic_catalog_postgres/repositories/drafts.py +156 -0
- nl2data_semantic_catalog_postgres/repositories/evidence.py +415 -0
- nl2data_semantic_catalog_postgres/repositories/publications.py +486 -0
- nl2data_semantic_catalog_postgres/repositories/snapshots.py +372 -0
- nl2data_semantic_catalog_postgres/schema.py +337 -0
- nl2data_semantic_catalog_postgres/sql.py +520 -0
- nl2data_semantic_catalog_postgres/store.py +716 -0
- nl2data_semantic_catalog_postgres/unit_of_work.py +589 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/METADATA +76 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/RECORD +34 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/WHEEL +5 -0
- nl2data_semantic_catalog_postgres-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""Versioned PostgreSQL schema for the durable semantic catalog.
|
|
2
|
+
|
|
3
|
+
The catalog owns its own schema namespace (the configured ``namespace``),
|
|
4
|
+
separate from the workflow state store: workflow tables coordinate execution,
|
|
5
|
+
catalog tables hold versioned control-plane content. Records are scoped by
|
|
6
|
+
opaque tenant scope fingerprints exactly like the workflow store - raw tenant
|
|
7
|
+
identifiers are never keys.
|
|
8
|
+
|
|
9
|
+
Migrations are additive and versioned: version ``1`` creates the snapshot,
|
|
10
|
+
proposal-set, Bundle publication, pointer, history, event, and schema-metadata
|
|
11
|
+
surface. Version ``2`` adds durable assembly drafts and immutable publication
|
|
12
|
+
lifecycle records. Version ``3`` adds Verification Suite evidence. Version ``4``
|
|
13
|
+
adds publication audit evidence bindings and the bounded assembly
|
|
14
|
+
audit-evidence trail. A runtime never applies a migration newer than the schema
|
|
15
|
+
version it supports, and a database reporting a newer schema than the runtime
|
|
16
|
+
supports is rejected without modification.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
#: The newest schema version this runtime understands.
|
|
22
|
+
SUPPORTED_SCHEMA_VERSION = 4
|
|
23
|
+
|
|
24
|
+
#: Migration steps keyed by the schema version they produce. Each entry is
|
|
25
|
+
#: applied inside one transaction and only when the persisted version is
|
|
26
|
+
#: exactly ``version - 1``, so a partial deployment can never be guessed at.
|
|
27
|
+
#: Templates use the ``{schema}`` placeholder (the configured namespace).
|
|
28
|
+
MIGRATIONS: dict[int, tuple[str, ...]] = {
|
|
29
|
+
1: (
|
|
30
|
+
# Catalog-owned metadata; named distinctly from the workflow store's
|
|
31
|
+
# ``schema_metadata`` so one deployment may share a namespace safely.
|
|
32
|
+
"""
|
|
33
|
+
CREATE TABLE IF NOT EXISTS {schema}.catalog_schema_metadata (
|
|
34
|
+
key TEXT PRIMARY KEY,
|
|
35
|
+
value TEXT NOT NULL,
|
|
36
|
+
updated_at TIMESTAMPTZ NOT NULL
|
|
37
|
+
)
|
|
38
|
+
""",
|
|
39
|
+
"""
|
|
40
|
+
CREATE TABLE IF NOT EXISTS {schema}.metadata_snapshots (
|
|
41
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
42
|
+
snapshot_fingerprint TEXT NOT NULL,
|
|
43
|
+
source_id TEXT NOT NULL,
|
|
44
|
+
state TEXT NOT NULL,
|
|
45
|
+
schema_version INTEGER NOT NULL,
|
|
46
|
+
envelope TEXT NOT NULL,
|
|
47
|
+
discovered_at TIMESTAMPTZ NOT NULL,
|
|
48
|
+
retained_until TIMESTAMPTZ,
|
|
49
|
+
activated_at TIMESTAMPTZ,
|
|
50
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
51
|
+
PRIMARY KEY (scope_namespace, snapshot_fingerprint)
|
|
52
|
+
)
|
|
53
|
+
""",
|
|
54
|
+
"""
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_source
|
|
56
|
+
ON {schema}.metadata_snapshots (scope_namespace, source_id, state)
|
|
57
|
+
""",
|
|
58
|
+
"""
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_retention
|
|
60
|
+
ON {schema}.metadata_snapshots (scope_namespace, retained_until)
|
|
61
|
+
""",
|
|
62
|
+
"""
|
|
63
|
+
CREATE TABLE IF NOT EXISTS {schema}.snapshot_pointers (
|
|
64
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
65
|
+
source_id TEXT NOT NULL,
|
|
66
|
+
snapshot_fingerprint TEXT NOT NULL,
|
|
67
|
+
schema_version INTEGER NOT NULL,
|
|
68
|
+
activated_at TIMESTAMPTZ NOT NULL,
|
|
69
|
+
PRIMARY KEY (scope_namespace, source_id),
|
|
70
|
+
FOREIGN KEY (scope_namespace, snapshot_fingerprint)
|
|
71
|
+
REFERENCES {schema}.metadata_snapshots
|
|
72
|
+
(scope_namespace, snapshot_fingerprint)
|
|
73
|
+
ON DELETE RESTRICT
|
|
74
|
+
)
|
|
75
|
+
""",
|
|
76
|
+
"""
|
|
77
|
+
CREATE TABLE IF NOT EXISTS {schema}.proposal_sets (
|
|
78
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
79
|
+
snapshot_fingerprint TEXT NOT NULL,
|
|
80
|
+
schema_version INTEGER NOT NULL,
|
|
81
|
+
envelope TEXT NOT NULL,
|
|
82
|
+
saved_at TIMESTAMPTZ NOT NULL,
|
|
83
|
+
PRIMARY KEY (scope_namespace, snapshot_fingerprint),
|
|
84
|
+
FOREIGN KEY (scope_namespace, snapshot_fingerprint)
|
|
85
|
+
REFERENCES {schema}.metadata_snapshots
|
|
86
|
+
(scope_namespace, snapshot_fingerprint)
|
|
87
|
+
ON DELETE CASCADE
|
|
88
|
+
)
|
|
89
|
+
""",
|
|
90
|
+
"""
|
|
91
|
+
CREATE TABLE IF NOT EXISTS {schema}.bundle_publications (
|
|
92
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
93
|
+
bundle_id TEXT NOT NULL,
|
|
94
|
+
model_version TEXT NOT NULL,
|
|
95
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
96
|
+
schema_version INTEGER NOT NULL,
|
|
97
|
+
envelope TEXT NOT NULL,
|
|
98
|
+
published_at TIMESTAMPTZ NOT NULL,
|
|
99
|
+
PRIMARY KEY (scope_namespace, bundle_id, model_version),
|
|
100
|
+
UNIQUE (scope_namespace, bundle_id, bundle_fingerprint)
|
|
101
|
+
)
|
|
102
|
+
""",
|
|
103
|
+
"""
|
|
104
|
+
CREATE INDEX IF NOT EXISTS idx_publications_published
|
|
105
|
+
ON {schema}.bundle_publications (scope_namespace, published_at)
|
|
106
|
+
""",
|
|
107
|
+
"""
|
|
108
|
+
CREATE TABLE IF NOT EXISTS {schema}.bundle_pointers (
|
|
109
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
110
|
+
bundle_id TEXT NOT NULL,
|
|
111
|
+
model_version TEXT NOT NULL,
|
|
112
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
113
|
+
schema_version INTEGER NOT NULL,
|
|
114
|
+
activated_at TIMESTAMPTZ NOT NULL,
|
|
115
|
+
activation_sequence BIGINT NOT NULL,
|
|
116
|
+
PRIMARY KEY (scope_namespace, bundle_id),
|
|
117
|
+
FOREIGN KEY (scope_namespace, bundle_id, model_version)
|
|
118
|
+
REFERENCES {schema}.bundle_publications
|
|
119
|
+
(scope_namespace, bundle_id, model_version)
|
|
120
|
+
ON DELETE RESTRICT
|
|
121
|
+
)
|
|
122
|
+
""",
|
|
123
|
+
"""
|
|
124
|
+
CREATE TABLE IF NOT EXISTS {schema}.bundle_history (
|
|
125
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
126
|
+
bundle_id TEXT NOT NULL,
|
|
127
|
+
position BIGINT NOT NULL,
|
|
128
|
+
model_version TEXT NOT NULL,
|
|
129
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
130
|
+
schema_version INTEGER NOT NULL,
|
|
131
|
+
activated_at TIMESTAMPTZ NOT NULL,
|
|
132
|
+
deactivated_at TIMESTAMPTZ NOT NULL,
|
|
133
|
+
PRIMARY KEY (scope_namespace, bundle_id, position),
|
|
134
|
+
FOREIGN KEY (scope_namespace, bundle_id, model_version)
|
|
135
|
+
REFERENCES {schema}.bundle_publications
|
|
136
|
+
(scope_namespace, bundle_id, model_version)
|
|
137
|
+
ON DELETE RESTRICT
|
|
138
|
+
)
|
|
139
|
+
""",
|
|
140
|
+
"""
|
|
141
|
+
CREATE INDEX IF NOT EXISTS idx_bundle_history_position
|
|
142
|
+
ON {schema}.bundle_history (scope_namespace, bundle_id, position)
|
|
143
|
+
""",
|
|
144
|
+
"""
|
|
145
|
+
CREATE TABLE IF NOT EXISTS {schema}.lifecycle_events (
|
|
146
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
147
|
+
event_id TEXT NOT NULL,
|
|
148
|
+
kind TEXT NOT NULL,
|
|
149
|
+
member_id TEXT,
|
|
150
|
+
schema_version INTEGER NOT NULL,
|
|
151
|
+
payload TEXT NOT NULL,
|
|
152
|
+
occurred_at TIMESTAMPTZ NOT NULL,
|
|
153
|
+
PRIMARY KEY (scope_namespace, event_id)
|
|
154
|
+
)
|
|
155
|
+
""",
|
|
156
|
+
"""
|
|
157
|
+
CREATE INDEX IF NOT EXISTS idx_events_occurred
|
|
158
|
+
ON {schema}.lifecycle_events (scope_namespace, occurred_at)
|
|
159
|
+
""",
|
|
160
|
+
),
|
|
161
|
+
2: (
|
|
162
|
+
"""
|
|
163
|
+
CREATE TABLE IF NOT EXISTS {schema}.assembly_drafts (
|
|
164
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
165
|
+
draft_id TEXT NOT NULL,
|
|
166
|
+
bundle_id TEXT NOT NULL,
|
|
167
|
+
source_id TEXT NOT NULL,
|
|
168
|
+
draft_revision BIGINT NOT NULL,
|
|
169
|
+
state TEXT NOT NULL,
|
|
170
|
+
schema_version INTEGER NOT NULL,
|
|
171
|
+
envelope TEXT NOT NULL,
|
|
172
|
+
updated_at TIMESTAMPTZ NOT NULL,
|
|
173
|
+
PRIMARY KEY (scope_namespace, draft_id)
|
|
174
|
+
)
|
|
175
|
+
""",
|
|
176
|
+
"""
|
|
177
|
+
CREATE INDEX IF NOT EXISTS idx_assembly_drafts_bundle
|
|
178
|
+
ON {schema}.assembly_drafts (scope_namespace, bundle_id, updated_at)
|
|
179
|
+
""",
|
|
180
|
+
"""
|
|
181
|
+
CREATE TABLE IF NOT EXISTS {schema}.accepted_assertion_manifests (
|
|
182
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
183
|
+
bundle_id TEXT NOT NULL,
|
|
184
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
185
|
+
schema_version INTEGER NOT NULL,
|
|
186
|
+
envelope TEXT NOT NULL,
|
|
187
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
188
|
+
PRIMARY KEY (scope_namespace, bundle_id, bundle_fingerprint)
|
|
189
|
+
)
|
|
190
|
+
""",
|
|
191
|
+
"""
|
|
192
|
+
CREATE TABLE IF NOT EXISTS {schema}.publish_audits (
|
|
193
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
194
|
+
bundle_id TEXT NOT NULL,
|
|
195
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
196
|
+
audit_id TEXT NOT NULL,
|
|
197
|
+
idempotency_key TEXT,
|
|
198
|
+
schema_version INTEGER NOT NULL,
|
|
199
|
+
envelope TEXT NOT NULL,
|
|
200
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
201
|
+
PRIMARY KEY (scope_namespace, bundle_id, bundle_fingerprint),
|
|
202
|
+
UNIQUE (scope_namespace, audit_id),
|
|
203
|
+
UNIQUE (scope_namespace, idempotency_key)
|
|
204
|
+
)
|
|
205
|
+
""",
|
|
206
|
+
"""
|
|
207
|
+
CREATE TABLE IF NOT EXISTS {schema}.published_versions (
|
|
208
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
209
|
+
bundle_id TEXT NOT NULL,
|
|
210
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
211
|
+
model_version TEXT NOT NULL,
|
|
212
|
+
lifecycle_state TEXT NOT NULL,
|
|
213
|
+
predecessor_fingerprint TEXT,
|
|
214
|
+
successor_fingerprint TEXT,
|
|
215
|
+
audit_id TEXT,
|
|
216
|
+
published_at TIMESTAMPTZ NOT NULL,
|
|
217
|
+
PRIMARY KEY (scope_namespace, bundle_id, bundle_fingerprint),
|
|
218
|
+
UNIQUE (scope_namespace, bundle_id, model_version)
|
|
219
|
+
)
|
|
220
|
+
""",
|
|
221
|
+
"""
|
|
222
|
+
CREATE INDEX IF NOT EXISTS idx_published_versions_order
|
|
223
|
+
ON {schema}.published_versions
|
|
224
|
+
(scope_namespace, bundle_id, published_at, model_version)
|
|
225
|
+
""",
|
|
226
|
+
"""
|
|
227
|
+
CREATE TABLE IF NOT EXISTS {schema}.supersession_edges (
|
|
228
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
229
|
+
bundle_id TEXT NOT NULL,
|
|
230
|
+
predecessor_fingerprint TEXT NOT NULL,
|
|
231
|
+
successor_fingerprint TEXT NOT NULL,
|
|
232
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
233
|
+
PRIMARY KEY (scope_namespace, bundle_id, successor_fingerprint),
|
|
234
|
+
UNIQUE (scope_namespace, bundle_id, predecessor_fingerprint)
|
|
235
|
+
)
|
|
236
|
+
""",
|
|
237
|
+
),
|
|
238
|
+
3: (
|
|
239
|
+
"""
|
|
240
|
+
CREATE TABLE IF NOT EXISTS {schema}.verification_suite_evidence (
|
|
241
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
242
|
+
bundle_id TEXT NOT NULL,
|
|
243
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
244
|
+
evidence_fingerprint TEXT NOT NULL,
|
|
245
|
+
schema_version INTEGER NOT NULL,
|
|
246
|
+
envelope TEXT NOT NULL,
|
|
247
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
248
|
+
PRIMARY KEY (scope_namespace, bundle_id, bundle_fingerprint),
|
|
249
|
+
UNIQUE (scope_namespace, evidence_fingerprint),
|
|
250
|
+
FOREIGN KEY (scope_namespace, bundle_id, bundle_fingerprint)
|
|
251
|
+
REFERENCES {schema}.bundle_publications
|
|
252
|
+
(scope_namespace, bundle_id, bundle_fingerprint)
|
|
253
|
+
ON DELETE CASCADE
|
|
254
|
+
)
|
|
255
|
+
""",
|
|
256
|
+
),
|
|
257
|
+
4: (
|
|
258
|
+
# The publication audit-evidence binding persists atomically with
|
|
259
|
+
# its publication and dies with it (CASCADE), exactly like the
|
|
260
|
+
# verification evidence it cross-links to.
|
|
261
|
+
"""
|
|
262
|
+
CREATE TABLE IF NOT EXISTS {schema}.publication_audit_evidence (
|
|
263
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
264
|
+
bundle_id TEXT NOT NULL,
|
|
265
|
+
bundle_fingerprint TEXT NOT NULL,
|
|
266
|
+
evidence_fingerprint TEXT NOT NULL,
|
|
267
|
+
schema_version INTEGER NOT NULL,
|
|
268
|
+
envelope TEXT NOT NULL,
|
|
269
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
270
|
+
PRIMARY KEY (scope_namespace, bundle_id, bundle_fingerprint),
|
|
271
|
+
UNIQUE (scope_namespace, evidence_fingerprint),
|
|
272
|
+
FOREIGN KEY (scope_namespace, bundle_id, bundle_fingerprint)
|
|
273
|
+
REFERENCES {schema}.bundle_publications
|
|
274
|
+
(scope_namespace, bundle_id, bundle_fingerprint)
|
|
275
|
+
ON DELETE CASCADE
|
|
276
|
+
)
|
|
277
|
+
""",
|
|
278
|
+
# One bounded envelope per audit-evidence entry, scoped by the
|
|
279
|
+
# opaque tenant namespace; the entry fingerprint column is the
|
|
280
|
+
# independent tamper witness recomputed on every read.
|
|
281
|
+
"""
|
|
282
|
+
CREATE TABLE IF NOT EXISTS {schema}.assembly_audit_evidence (
|
|
283
|
+
scope_namespace TEXT NOT NULL DEFAULT '',
|
|
284
|
+
event_id TEXT NOT NULL,
|
|
285
|
+
event_kind TEXT NOT NULL,
|
|
286
|
+
subject_kind TEXT NOT NULL,
|
|
287
|
+
subject_reference TEXT NOT NULL,
|
|
288
|
+
draft_id TEXT,
|
|
289
|
+
draft_revision BIGINT,
|
|
290
|
+
assertion_id TEXT,
|
|
291
|
+
bundle_fingerprint TEXT,
|
|
292
|
+
lifecycle_reference TEXT,
|
|
293
|
+
entry_fingerprint TEXT NOT NULL,
|
|
294
|
+
schema_version INTEGER NOT NULL,
|
|
295
|
+
envelope TEXT NOT NULL,
|
|
296
|
+
occurred_at TIMESTAMPTZ NOT NULL,
|
|
297
|
+
PRIMARY KEY (scope_namespace, event_id)
|
|
298
|
+
)
|
|
299
|
+
""",
|
|
300
|
+
"""
|
|
301
|
+
CREATE INDEX IF NOT EXISTS idx_audit_evidence_subject
|
|
302
|
+
ON {schema}.assembly_audit_evidence
|
|
303
|
+
(scope_namespace, subject_kind, subject_reference, occurred_at, event_id)
|
|
304
|
+
""",
|
|
305
|
+
"""
|
|
306
|
+
CREATE INDEX IF NOT EXISTS idx_audit_evidence_draft
|
|
307
|
+
ON {schema}.assembly_audit_evidence
|
|
308
|
+
(scope_namespace, draft_id, occurred_at, event_id)
|
|
309
|
+
""",
|
|
310
|
+
"""
|
|
311
|
+
CREATE INDEX IF NOT EXISTS idx_audit_evidence_assertion
|
|
312
|
+
ON {schema}.assembly_audit_evidence
|
|
313
|
+
(scope_namespace, assertion_id)
|
|
314
|
+
""",
|
|
315
|
+
"""
|
|
316
|
+
CREATE INDEX IF NOT EXISTS idx_audit_evidence_bundle
|
|
317
|
+
ON {schema}.assembly_audit_evidence
|
|
318
|
+
(scope_namespace, bundle_fingerprint)
|
|
319
|
+
""",
|
|
320
|
+
# Predecessor links live inside the canonical envelope; the GIN
|
|
321
|
+
# index keeps predecessor-scoped lookups bounded without denormalizing
|
|
322
|
+
# the immutable payload. Only the jsonb expression is indexed: plain
|
|
323
|
+
# text columns have no default GIN operator class, and the namespace
|
|
324
|
+
# equality is served by the scope-leading btree indexes via BitmapAnd.
|
|
325
|
+
"""
|
|
326
|
+
CREATE INDEX IF NOT EXISTS idx_audit_evidence_predecessors
|
|
327
|
+
ON {schema}.assembly_audit_evidence USING gin (
|
|
328
|
+
(envelope::jsonb -> 'payload' -> 'predecessor_event_ids')
|
|
329
|
+
)
|
|
330
|
+
""",
|
|
331
|
+
"""
|
|
332
|
+
CREATE INDEX IF NOT EXISTS idx_audit_evidence_retention
|
|
333
|
+
ON {schema}.assembly_audit_evidence (scope_namespace, occurred_at)
|
|
334
|
+
""",
|
|
335
|
+
),
|
|
336
|
+
}
|
|
337
|
+
|