ghcr-manager 0.9.7 → 0.9.8
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.
Potentially problematic release.
This version of ghcr-manager might be problematic. Click here for more details.
- package/CHANGELOG.md +39 -1
- package/LICENSE +1 -1
- package/README.md +37 -56
- package/dist/cleanup-summary/_cleanup-summary-markdown.js +7 -10
- package/dist/cleanup-summary/_cleanup-summary.d.ts +3 -4
- package/dist/cleanup-summary/_cleanup-summary.js +2 -3
- package/dist/cli/_cleanup-command.js +1 -1
- package/dist/cli/_tag-selector-resolver.js +29 -9
- package/dist/cli/index.js +0 -4
- package/dist/core/_types.d.ts +1 -6
- package/dist/core/_types.js +1 -1
- package/dist/db/_db-merge-scan-copy.js +3 -2
- package/dist/db/_manifest-reachability.js +47 -8
- package/dist/db/_scan-writer.js +1 -13
- package/dist/db/planner/_planner-direct-target-root-options.d.ts +11 -0
- package/dist/db/planner/_planner-direct-target-root-options.js +1 -0
- package/dist/db/planner/_planner-direct-target-root-tag-filters.d.ts +9 -0
- package/dist/db/planner/_planner-direct-target-root-tag-filters.js +42 -0
- package/dist/db/planner/_planner-direct-target-roots-combined-sql.d.ts +7 -0
- package/dist/db/planner/_planner-direct-target-roots-combined-sql.js +198 -0
- package/dist/db/planner/_planner-direct-target-roots-combined.d.ts +4 -0
- package/dist/db/planner/_planner-direct-target-roots-combined.js +10 -0
- package/dist/db/planner/_planner-direct-target-roots-tagged.d.ts +4 -0
- package/dist/db/planner/_planner-direct-target-roots-tagged.js +125 -0
- package/dist/db/planner/_planner-direct-target-roots.d.ts +2 -12
- package/dist/db/planner/_planner-direct-target-roots.js +8 -203
- package/dist/db/planner/_planner-direct-target-tags.js +3 -4
- package/dist/db/planner/_planner-output.js +28 -8
- package/dist/db/planner/_planner-plan-artifacts-blocked-roots-sql.d.ts +1 -0
- package/dist/db/planner/_planner-plan-artifacts-blocked-roots-sql.js +65 -0
- package/dist/db/planner/_planner-plan-artifacts-closure-sql.d.ts +1 -0
- package/dist/db/planner/_planner-plan-artifacts-closure-sql.js +195 -0
- package/dist/db/planner/_planner-plan-artifacts-supported-untag-only-sql.d.ts +1 -0
- package/dist/db/planner/_planner-plan-artifacts-supported-untag-only-sql.js +86 -0
- package/dist/db/planner/_planner-plan-artifacts.js +26 -128
- package/dist/db/planner/_planner-sql.js +13 -2
- package/dist/db/planner/_planner-types.d.ts +2 -0
- package/dist/db/planner/_planner-types.js +1 -0
- package/dist/execute/_plan-executor.js +7 -11
- package/dist/execute/_types.d.ts +2 -19
- package/dist/execute/_untag-client.d.ts +2 -2
- package/dist/execute/_untag-client.js +1 -42
- package/dist/execute/index.d.ts +1 -1
- package/dist/ingest/github/_manifest-kind.d.ts +6 -0
- package/dist/ingest/github/_manifest-kind.js +16 -2
- package/package.json +16 -10
- package/resources/sql/schema/001_schema.sql +14 -4
- package/dist/cli/_untag-command.d.ts +0 -1
- package/dist/cli/_untag-command.js +0 -58
- package/dist/db/_manifest-kind-refinement.d.ts +0 -2
- package/dist/db/_manifest-kind-refinement.js +0 -43
- package/resources/sql/views/002_v_scan_root_manifests.sql +0 -44
- package/resources/sql/views/003_v_digest_tag_relations.sql +0 -50
- package/resources/sql/views/004_v_cleanup_root_closure_members.sql +0 -101
- package/resources/sql/views/005_v_cleanup_blocking_overlaps.sql +0 -42
- package/resources/sql/views/006_v_cleanup_root_decision_readable.sql +0 -67
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { DeletePlanValidationReasonCodes, DeletePlanValidationStatuses } from "./_planner-types.js";
|
|
2
2
|
export function buildPlanOutputs(directTargetTags, directTargetRoots, planArtifacts) {
|
|
3
3
|
const rootDecisions = buildRootDecisions(directTargetRoots, planArtifacts);
|
|
4
|
-
const
|
|
4
|
+
const fullyDeletableDigests = new Set(rootDecisions
|
|
5
|
+
.filter((decision) => decision.validationStatus === DeletePlanValidationStatuses.fullyDeletable)
|
|
6
|
+
.map((decision) => decision.digest));
|
|
7
|
+
const blockedDigests = new Set(rootDecisions
|
|
8
|
+
.filter((decision) => decision.validationStatus === DeletePlanValidationStatuses.blocked)
|
|
9
|
+
.map((decision) => decision.digest));
|
|
10
|
+
const blockedRoots = planArtifacts.blockedRoots.filter((blockedRoot) => blockedDigests.has(blockedRoot.blockedDigest));
|
|
11
|
+
const protectedRoots = buildProtectedRoots(blockedRoots);
|
|
5
12
|
return {
|
|
6
13
|
directTargetTags,
|
|
7
14
|
directTargetRoots,
|
|
8
15
|
rootDecisions,
|
|
9
16
|
protectedRoots,
|
|
10
17
|
closureManifests: planArtifacts.closureManifests,
|
|
11
|
-
blockedRoots
|
|
12
|
-
fullyDeletableRoots: planArtifacts.fullyDeletableRoots,
|
|
18
|
+
blockedRoots,
|
|
19
|
+
fullyDeletableRoots: planArtifacts.fullyDeletableRoots.filter((root) => fullyDeletableDigests.has(root.digest)),
|
|
13
20
|
collateralTags: []
|
|
14
21
|
};
|
|
15
22
|
}
|
|
16
23
|
export function buildRootDecisions(directTargetRoots, planArtifacts) {
|
|
17
24
|
const fullyDeletableDigests = new Set(planArtifacts.fullyDeletableRoots.map((root) => root.digest));
|
|
25
|
+
const supportedUntagOnlyRootDigests = planArtifacts.supportedUntagOnlyRootDigests;
|
|
18
26
|
const blockedRootByDigest = new Map();
|
|
19
27
|
for (const blockedRoot of planArtifacts.blockedRoots) {
|
|
20
28
|
if (!blockedRootByDigest.has(blockedRoot.blockedDigest)) {
|
|
@@ -22,16 +30,21 @@ export function buildRootDecisions(directTargetRoots, planArtifacts) {
|
|
|
22
30
|
}
|
|
23
31
|
}
|
|
24
32
|
return directTargetRoots.map((root) => {
|
|
25
|
-
|
|
33
|
+
const blockedRoot = blockedRootByDigest.get(root.digest);
|
|
34
|
+
if (_isUntagOnly(root, blockedRoot, supportedUntagOnlyRootDigests)) {
|
|
26
35
|
return {
|
|
27
36
|
versionId: root.versionId,
|
|
28
37
|
digest: root.digest,
|
|
29
38
|
manifestKind: root.manifestKind,
|
|
30
|
-
selectionMode:
|
|
39
|
+
selectionMode: "untag-only",
|
|
31
40
|
selectionReason: root.reason,
|
|
32
41
|
validationStatus: DeletePlanValidationStatuses.untagOnly,
|
|
33
|
-
validationReasonCode:
|
|
34
|
-
|
|
42
|
+
validationReasonCode: root.selectionMode === "untag-only"
|
|
43
|
+
? DeletePlanValidationReasonCodes.untagOnlyPartialTagMatch
|
|
44
|
+
: DeletePlanValidationReasonCodes.untagOnlyRetainedManifest,
|
|
45
|
+
validationReason: root.selectionMode === "untag-only"
|
|
46
|
+
? "matched tags cover only part of this root's tag set, so the version is retained and only those tags can be detached"
|
|
47
|
+
: "selected tags can be detached, but the manifest itself must remain because surviving tags still need it"
|
|
35
48
|
};
|
|
36
49
|
}
|
|
37
50
|
if (fullyDeletableDigests.has(root.digest)) {
|
|
@@ -46,7 +59,6 @@ export function buildRootDecisions(directTargetRoots, planArtifacts) {
|
|
|
46
59
|
validationReason: "selected tags cover the whole root and its manifest closure does not overlap any retained root"
|
|
47
60
|
};
|
|
48
61
|
}
|
|
49
|
-
const blockedRoot = blockedRootByDigest.get(root.digest);
|
|
50
62
|
return {
|
|
51
63
|
versionId: root.versionId,
|
|
52
64
|
digest: root.digest,
|
|
@@ -89,3 +101,11 @@ export function buildBlockedValidationReason(blockedRoot) {
|
|
|
89
101
|
}
|
|
90
102
|
return `blocked because retained root ${blockedRoot.blockingDigest} still requires shared manifest ${blockedRoot.overlapDigest}`;
|
|
91
103
|
}
|
|
104
|
+
function _isUntagOnly(root, blockedRoot, supportedUntagOnlyRootDigests) {
|
|
105
|
+
if (root.selectionMode === "untag-only") {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return (root.reason === "delete-tags-all-tags-selected" &&
|
|
109
|
+
((blockedRoot !== undefined && blockedRoot.overlapDigest === root.digest) ||
|
|
110
|
+
supportedUntagOnlyRootDigests.has(root.digest)));
|
|
111
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const _LIST_BLOCKED_ROOTS_SQL = "\n WITH selected_graphs AS (\n SELECT DISTINCT\n manifest_graphs.graph_id\n FROM temp_direct_target_roots dtr\n CROSS JOIN manifest_graphs\n WHERE manifest_graphs.scan_id = ?\n AND manifest_graphs.digest = dtr.root_digest\n ),\n retained_tagged_manifests AS (\n SELECT\n m.version_id AS tagged_version_id,\n m.digest AS tagged_digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs\n CROSS JOIN manifests m\n JOIN tags t\n ON t.scan_id = m.scan_id\n AND t.version_id = m.version_id\n AND t.is_digest_tag = 0\n WHERE manifest_graphs.scan_id = m.scan_id\n AND selected_graphs.graph_id = manifest_graphs.graph_id\n AND manifest_graphs.digest = m.digest\n AND m.scan_id = ?\n AND NOT EXISTS (\n SELECT 1\n FROM temp_direct_target_roots dtr\n WHERE dtr.root_digest = m.digest\n )\n ),\n ranked_blocks AS (\n SELECT\n dtr.root_version_id AS blocked_version_id,\n dtr.root_digest AS blocked_digest,\n retained.tagged_version_id AS blocking_version_id,\n retained.tagged_digest AS blocking_digest,\n dtr.root_digest AS overlap_digest,\n dtr.root_manifest_kind AS overlap_manifest_kind,\n 'overlap-with-retained-root' AS block_reason,\n ROW_NUMBER() OVER (\n PARTITION BY dtr.root_digest, retained.tagged_digest\n ORDER BY\n retained_overlap.min_distance,\n dtr.root_digest\n ) AS rn\n FROM temp_direct_target_roots dtr\n JOIN retained_tagged_manifests retained\n ON retained.tagged_digest <> dtr.root_digest\n JOIN manifest_reachability retained_overlap\n ON retained_overlap.scan_id = ?\n AND retained_overlap.ancestor_digest = retained.tagged_digest\n AND retained_overlap.descendant_digest = dtr.root_digest\n )\n SELECT\n blocked_version_id,\n blocked_digest,\n blocking_version_id,\n blocking_digest,\n overlap_digest,\n overlap_manifest_kind,\n block_reason\n FROM ranked_blocks\n WHERE rn = 1\n ORDER BY blocked_digest, blocking_digest, overlap_digest\n";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export const _LIST_BLOCKED_ROOTS_SQL = `
|
|
2
|
+
WITH selected_graphs AS (
|
|
3
|
+
SELECT DISTINCT
|
|
4
|
+
manifest_graphs.graph_id
|
|
5
|
+
FROM temp_direct_target_roots dtr
|
|
6
|
+
CROSS JOIN manifest_graphs
|
|
7
|
+
WHERE manifest_graphs.scan_id = ?
|
|
8
|
+
AND manifest_graphs.digest = dtr.root_digest
|
|
9
|
+
),
|
|
10
|
+
retained_tagged_manifests AS (
|
|
11
|
+
SELECT
|
|
12
|
+
m.version_id AS tagged_version_id,
|
|
13
|
+
m.digest AS tagged_digest
|
|
14
|
+
FROM selected_graphs
|
|
15
|
+
CROSS JOIN manifest_graphs
|
|
16
|
+
CROSS JOIN manifests m
|
|
17
|
+
JOIN tags t
|
|
18
|
+
ON t.scan_id = m.scan_id
|
|
19
|
+
AND t.version_id = m.version_id
|
|
20
|
+
AND t.is_digest_tag = 0
|
|
21
|
+
WHERE manifest_graphs.scan_id = m.scan_id
|
|
22
|
+
AND selected_graphs.graph_id = manifest_graphs.graph_id
|
|
23
|
+
AND manifest_graphs.digest = m.digest
|
|
24
|
+
AND m.scan_id = ?
|
|
25
|
+
AND NOT EXISTS (
|
|
26
|
+
SELECT 1
|
|
27
|
+
FROM temp_direct_target_roots dtr
|
|
28
|
+
WHERE dtr.root_digest = m.digest
|
|
29
|
+
)
|
|
30
|
+
),
|
|
31
|
+
ranked_blocks AS (
|
|
32
|
+
SELECT
|
|
33
|
+
dtr.root_version_id AS blocked_version_id,
|
|
34
|
+
dtr.root_digest AS blocked_digest,
|
|
35
|
+
retained.tagged_version_id AS blocking_version_id,
|
|
36
|
+
retained.tagged_digest AS blocking_digest,
|
|
37
|
+
dtr.root_digest AS overlap_digest,
|
|
38
|
+
dtr.root_manifest_kind AS overlap_manifest_kind,
|
|
39
|
+
'overlap-with-retained-root' AS block_reason,
|
|
40
|
+
ROW_NUMBER() OVER (
|
|
41
|
+
PARTITION BY dtr.root_digest, retained.tagged_digest
|
|
42
|
+
ORDER BY
|
|
43
|
+
retained_overlap.min_distance,
|
|
44
|
+
dtr.root_digest
|
|
45
|
+
) AS rn
|
|
46
|
+
FROM temp_direct_target_roots dtr
|
|
47
|
+
JOIN retained_tagged_manifests retained
|
|
48
|
+
ON retained.tagged_digest <> dtr.root_digest
|
|
49
|
+
JOIN manifest_reachability retained_overlap
|
|
50
|
+
ON retained_overlap.scan_id = ?
|
|
51
|
+
AND retained_overlap.ancestor_digest = retained.tagged_digest
|
|
52
|
+
AND retained_overlap.descendant_digest = dtr.root_digest
|
|
53
|
+
)
|
|
54
|
+
SELECT
|
|
55
|
+
blocked_version_id,
|
|
56
|
+
blocked_digest,
|
|
57
|
+
blocking_version_id,
|
|
58
|
+
blocking_digest,
|
|
59
|
+
overlap_digest,
|
|
60
|
+
overlap_manifest_kind,
|
|
61
|
+
block_reason
|
|
62
|
+
FROM ranked_blocks
|
|
63
|
+
WHERE rn = 1
|
|
64
|
+
ORDER BY blocked_digest, blocking_digest, overlap_digest
|
|
65
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const _LIST_CLOSURE_MANIFESTS_SQL = "\n WITH selected_graphs AS (\n SELECT DISTINCT\n manifest_graphs.graph_id\n FROM temp_direct_target_roots dtr\n CROSS JOIN manifest_graphs\n WHERE manifest_graphs.scan_id = ?\n AND manifest_graphs.digest = dtr.root_digest\n ),\n retained_tagged_manifests AS (\n SELECT DISTINCT\n m.version_id,\n m.digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs\n CROSS JOIN manifests m\n JOIN tags t\n ON t.scan_id = m.scan_id\n AND t.version_id = m.version_id\n AND t.is_digest_tag = 0\n WHERE manifest_graphs.scan_id = m.scan_id\n AND selected_graphs.graph_id = manifest_graphs.graph_id\n AND manifest_graphs.digest = m.digest\n AND m.scan_id = ?\n AND NOT EXISTS (\n SELECT 1\n FROM temp_direct_target_roots dtr\n WHERE dtr.root_digest = m.digest\n )\n ),\n retained_manifests AS (\n SELECT\n retained.version_id,\n retained.digest\n FROM retained_tagged_manifests retained\n\n UNION\n\n SELECT\n m.version_id,\n m.digest\n FROM retained_tagged_manifests retained\n CROSS JOIN manifest_reachability mr\n CROSS JOIN manifests m\n WHERE mr.scan_id = ?\n AND mr.ancestor_digest = retained.digest\n AND mr.min_distance > 0\n AND m.scan_id = ?\n AND m.digest = mr.descendant_digest\n ),\n direct_target_closure AS (\n SELECT\n dtr.root_version_id AS source_version_id,\n dtr.root_digest AS source_digest,\n dtr.root_version_id AS member_version_id,\n dtr.root_digest AS member_digest,\n dtr.root_manifest_kind AS member_manifest_kind,\n 0 AS hops_from_root\n FROM temp_direct_target_roots dtr\n\n UNION ALL\n\n SELECT\n dtr.root_version_id AS source_version_id,\n dtr.root_digest AS source_digest,\n m.version_id AS member_version_id,\n m.digest AS member_digest,\n m.manifest_kind AS member_manifest_kind,\n mr.min_distance AS hops_from_root\n FROM temp_direct_target_roots dtr\n CROSS JOIN manifest_reachability mr\n CROSS JOIN manifests m\n WHERE mr.scan_id = ?\n AND mr.ancestor_digest = dtr.root_digest\n AND mr.min_distance > 0\n AND m.scan_id = ?\n AND m.digest = mr.descendant_digest\n ),\n closure_seed AS (\n SELECT\n dtc.source_version_id,\n dtc.source_digest,\n dtc.member_digest,\n dtc.hops_from_root\n FROM direct_target_closure dtc\n WHERE dtc.hops_from_root = 0\n OR NOT EXISTS (\n SELECT 1\n FROM retained_manifests retained\n WHERE retained.digest = dtc.member_digest\n )\n ),\n undirected_edges AS (\n SELECT\n me.parent_digest AS source_digest,\n me.child_digest AS target_digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs parent_graph\n CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_parent\n WHERE parent_graph.scan_id = me.scan_id\n AND selected_graphs.graph_id = parent_graph.graph_id\n AND parent_graph.digest = me.parent_digest\n AND me.scan_id = ?\n\n UNION\n\n SELECT\n me.child_digest AS source_digest,\n me.parent_digest AS target_digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs child_graph\n CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_child\n WHERE child_graph.scan_id = me.scan_id\n AND selected_graphs.graph_id = child_graph.graph_id\n AND child_graph.digest = me.child_digest\n AND me.scan_id = ?\n ),\n delete_component_members AS (\n SELECT\n seed.source_version_id,\n seed.source_digest,\n seed.member_digest\n FROM closure_seed seed\n\n UNION\n\n SELECT\n walk.source_version_id,\n walk.source_digest,\n m.digest AS member_digest\n FROM delete_component_members walk\n JOIN undirected_edges edge\n ON edge.source_digest = walk.member_digest\n JOIN manifests m\n ON m.scan_id = ?\n AND m.digest = edge.target_digest\n WHERE NOT EXISTS (\n SELECT 1\n FROM retained_manifests retained\n WHERE retained.digest = m.digest\n )\n ),\n source_seed_hops AS (\n SELECT\n seed.source_digest,\n MAX(seed.hops_from_root) AS max_seed_hops\n FROM closure_seed seed\n GROUP BY seed.source_digest\n ),\n descendant_hops AS (\n SELECT\n dtc.source_digest,\n dtc.member_digest,\n MIN(dtc.hops_from_root) AS min_hops_from_root\n FROM direct_target_closure dtc\n WHERE dtc.hops_from_root > 0\n GROUP BY dtc.source_digest, dtc.member_digest\n )\n SELECT\n walk.source_version_id,\n walk.source_digest,\n MIN(member_manifest.version_id) AS member_version_id,\n walk.member_digest,\n MIN(member_manifest.manifest_kind) AS member_manifest_kind,\n CASE\n WHEN walk.member_digest = walk.source_digest\n THEN 0\n WHEN descendant_hops.min_hops_from_root IS NOT NULL\n THEN descendant_hops.min_hops_from_root\n ELSE source_seed_hops.max_seed_hops + 1\n END AS hops_from_root,\n CASE\n WHEN walk.member_digest = walk.source_digest\n THEN 'root'\n WHEN descendant_hops.min_hops_from_root IS NOT NULL\n THEN 'descendant'\n ELSE 'connected'\n END AS member_role\n FROM delete_component_members walk\n JOIN manifests member_manifest\n ON member_manifest.scan_id = ?\n AND member_manifest.digest = walk.member_digest\n JOIN source_seed_hops\n ON source_seed_hops.source_digest = walk.source_digest\n LEFT JOIN descendant_hops\n ON descendant_hops.source_digest = walk.source_digest\n AND descendant_hops.member_digest = walk.member_digest\n GROUP BY\n walk.source_version_id,\n walk.source_digest,\n walk.member_digest,\n descendant_hops.min_hops_from_root,\n source_seed_hops.max_seed_hops\n ORDER BY walk.source_digest, hops_from_root, walk.member_digest\n";
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
export const _LIST_CLOSURE_MANIFESTS_SQL = `
|
|
2
|
+
WITH selected_graphs AS (
|
|
3
|
+
SELECT DISTINCT
|
|
4
|
+
manifest_graphs.graph_id
|
|
5
|
+
FROM temp_direct_target_roots dtr
|
|
6
|
+
CROSS JOIN manifest_graphs
|
|
7
|
+
WHERE manifest_graphs.scan_id = ?
|
|
8
|
+
AND manifest_graphs.digest = dtr.root_digest
|
|
9
|
+
),
|
|
10
|
+
retained_tagged_manifests AS (
|
|
11
|
+
SELECT DISTINCT
|
|
12
|
+
m.version_id,
|
|
13
|
+
m.digest
|
|
14
|
+
FROM selected_graphs
|
|
15
|
+
CROSS JOIN manifest_graphs
|
|
16
|
+
CROSS JOIN manifests m
|
|
17
|
+
JOIN tags t
|
|
18
|
+
ON t.scan_id = m.scan_id
|
|
19
|
+
AND t.version_id = m.version_id
|
|
20
|
+
AND t.is_digest_tag = 0
|
|
21
|
+
WHERE manifest_graphs.scan_id = m.scan_id
|
|
22
|
+
AND selected_graphs.graph_id = manifest_graphs.graph_id
|
|
23
|
+
AND manifest_graphs.digest = m.digest
|
|
24
|
+
AND m.scan_id = ?
|
|
25
|
+
AND NOT EXISTS (
|
|
26
|
+
SELECT 1
|
|
27
|
+
FROM temp_direct_target_roots dtr
|
|
28
|
+
WHERE dtr.root_digest = m.digest
|
|
29
|
+
)
|
|
30
|
+
),
|
|
31
|
+
retained_manifests AS (
|
|
32
|
+
SELECT
|
|
33
|
+
retained.version_id,
|
|
34
|
+
retained.digest
|
|
35
|
+
FROM retained_tagged_manifests retained
|
|
36
|
+
|
|
37
|
+
UNION
|
|
38
|
+
|
|
39
|
+
SELECT
|
|
40
|
+
m.version_id,
|
|
41
|
+
m.digest
|
|
42
|
+
FROM retained_tagged_manifests retained
|
|
43
|
+
CROSS JOIN manifest_reachability mr
|
|
44
|
+
CROSS JOIN manifests m
|
|
45
|
+
WHERE mr.scan_id = ?
|
|
46
|
+
AND mr.ancestor_digest = retained.digest
|
|
47
|
+
AND mr.min_distance > 0
|
|
48
|
+
AND m.scan_id = ?
|
|
49
|
+
AND m.digest = mr.descendant_digest
|
|
50
|
+
),
|
|
51
|
+
direct_target_closure AS (
|
|
52
|
+
SELECT
|
|
53
|
+
dtr.root_version_id AS source_version_id,
|
|
54
|
+
dtr.root_digest AS source_digest,
|
|
55
|
+
dtr.root_version_id AS member_version_id,
|
|
56
|
+
dtr.root_digest AS member_digest,
|
|
57
|
+
dtr.root_manifest_kind AS member_manifest_kind,
|
|
58
|
+
0 AS hops_from_root
|
|
59
|
+
FROM temp_direct_target_roots dtr
|
|
60
|
+
|
|
61
|
+
UNION ALL
|
|
62
|
+
|
|
63
|
+
SELECT
|
|
64
|
+
dtr.root_version_id AS source_version_id,
|
|
65
|
+
dtr.root_digest AS source_digest,
|
|
66
|
+
m.version_id AS member_version_id,
|
|
67
|
+
m.digest AS member_digest,
|
|
68
|
+
m.manifest_kind AS member_manifest_kind,
|
|
69
|
+
mr.min_distance AS hops_from_root
|
|
70
|
+
FROM temp_direct_target_roots dtr
|
|
71
|
+
CROSS JOIN manifest_reachability mr
|
|
72
|
+
CROSS JOIN manifests m
|
|
73
|
+
WHERE mr.scan_id = ?
|
|
74
|
+
AND mr.ancestor_digest = dtr.root_digest
|
|
75
|
+
AND mr.min_distance > 0
|
|
76
|
+
AND m.scan_id = ?
|
|
77
|
+
AND m.digest = mr.descendant_digest
|
|
78
|
+
),
|
|
79
|
+
closure_seed AS (
|
|
80
|
+
SELECT
|
|
81
|
+
dtc.source_version_id,
|
|
82
|
+
dtc.source_digest,
|
|
83
|
+
dtc.member_digest,
|
|
84
|
+
dtc.hops_from_root
|
|
85
|
+
FROM direct_target_closure dtc
|
|
86
|
+
WHERE dtc.hops_from_root = 0
|
|
87
|
+
OR NOT EXISTS (
|
|
88
|
+
SELECT 1
|
|
89
|
+
FROM retained_manifests retained
|
|
90
|
+
WHERE retained.digest = dtc.member_digest
|
|
91
|
+
)
|
|
92
|
+
),
|
|
93
|
+
undirected_edges AS (
|
|
94
|
+
SELECT
|
|
95
|
+
me.parent_digest AS source_digest,
|
|
96
|
+
me.child_digest AS target_digest
|
|
97
|
+
FROM selected_graphs
|
|
98
|
+
CROSS JOIN manifest_graphs parent_graph
|
|
99
|
+
CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_parent
|
|
100
|
+
WHERE parent_graph.scan_id = me.scan_id
|
|
101
|
+
AND selected_graphs.graph_id = parent_graph.graph_id
|
|
102
|
+
AND parent_graph.digest = me.parent_digest
|
|
103
|
+
AND me.scan_id = ?
|
|
104
|
+
|
|
105
|
+
UNION
|
|
106
|
+
|
|
107
|
+
SELECT
|
|
108
|
+
me.child_digest AS source_digest,
|
|
109
|
+
me.parent_digest AS target_digest
|
|
110
|
+
FROM selected_graphs
|
|
111
|
+
CROSS JOIN manifest_graphs child_graph
|
|
112
|
+
CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_child
|
|
113
|
+
WHERE child_graph.scan_id = me.scan_id
|
|
114
|
+
AND selected_graphs.graph_id = child_graph.graph_id
|
|
115
|
+
AND child_graph.digest = me.child_digest
|
|
116
|
+
AND me.scan_id = ?
|
|
117
|
+
),
|
|
118
|
+
delete_component_members AS (
|
|
119
|
+
SELECT
|
|
120
|
+
seed.source_version_id,
|
|
121
|
+
seed.source_digest,
|
|
122
|
+
seed.member_digest
|
|
123
|
+
FROM closure_seed seed
|
|
124
|
+
|
|
125
|
+
UNION
|
|
126
|
+
|
|
127
|
+
SELECT
|
|
128
|
+
walk.source_version_id,
|
|
129
|
+
walk.source_digest,
|
|
130
|
+
m.digest AS member_digest
|
|
131
|
+
FROM delete_component_members walk
|
|
132
|
+
JOIN undirected_edges edge
|
|
133
|
+
ON edge.source_digest = walk.member_digest
|
|
134
|
+
JOIN manifests m
|
|
135
|
+
ON m.scan_id = ?
|
|
136
|
+
AND m.digest = edge.target_digest
|
|
137
|
+
WHERE NOT EXISTS (
|
|
138
|
+
SELECT 1
|
|
139
|
+
FROM retained_manifests retained
|
|
140
|
+
WHERE retained.digest = m.digest
|
|
141
|
+
)
|
|
142
|
+
),
|
|
143
|
+
source_seed_hops AS (
|
|
144
|
+
SELECT
|
|
145
|
+
seed.source_digest,
|
|
146
|
+
MAX(seed.hops_from_root) AS max_seed_hops
|
|
147
|
+
FROM closure_seed seed
|
|
148
|
+
GROUP BY seed.source_digest
|
|
149
|
+
),
|
|
150
|
+
descendant_hops AS (
|
|
151
|
+
SELECT
|
|
152
|
+
dtc.source_digest,
|
|
153
|
+
dtc.member_digest,
|
|
154
|
+
MIN(dtc.hops_from_root) AS min_hops_from_root
|
|
155
|
+
FROM direct_target_closure dtc
|
|
156
|
+
WHERE dtc.hops_from_root > 0
|
|
157
|
+
GROUP BY dtc.source_digest, dtc.member_digest
|
|
158
|
+
)
|
|
159
|
+
SELECT
|
|
160
|
+
walk.source_version_id,
|
|
161
|
+
walk.source_digest,
|
|
162
|
+
MIN(member_manifest.version_id) AS member_version_id,
|
|
163
|
+
walk.member_digest,
|
|
164
|
+
MIN(member_manifest.manifest_kind) AS member_manifest_kind,
|
|
165
|
+
CASE
|
|
166
|
+
WHEN walk.member_digest = walk.source_digest
|
|
167
|
+
THEN 0
|
|
168
|
+
WHEN descendant_hops.min_hops_from_root IS NOT NULL
|
|
169
|
+
THEN descendant_hops.min_hops_from_root
|
|
170
|
+
ELSE source_seed_hops.max_seed_hops + 1
|
|
171
|
+
END AS hops_from_root,
|
|
172
|
+
CASE
|
|
173
|
+
WHEN walk.member_digest = walk.source_digest
|
|
174
|
+
THEN 'root'
|
|
175
|
+
WHEN descendant_hops.min_hops_from_root IS NOT NULL
|
|
176
|
+
THEN 'descendant'
|
|
177
|
+
ELSE 'connected'
|
|
178
|
+
END AS member_role
|
|
179
|
+
FROM delete_component_members walk
|
|
180
|
+
JOIN manifests member_manifest
|
|
181
|
+
ON member_manifest.scan_id = ?
|
|
182
|
+
AND member_manifest.digest = walk.member_digest
|
|
183
|
+
JOIN source_seed_hops
|
|
184
|
+
ON source_seed_hops.source_digest = walk.source_digest
|
|
185
|
+
LEFT JOIN descendant_hops
|
|
186
|
+
ON descendant_hops.source_digest = walk.source_digest
|
|
187
|
+
AND descendant_hops.member_digest = walk.member_digest
|
|
188
|
+
GROUP BY
|
|
189
|
+
walk.source_version_id,
|
|
190
|
+
walk.source_digest,
|
|
191
|
+
walk.member_digest,
|
|
192
|
+
descendant_hops.min_hops_from_root,
|
|
193
|
+
source_seed_hops.max_seed_hops
|
|
194
|
+
ORDER BY walk.source_digest, hops_from_root, walk.member_digest
|
|
195
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const _LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL = "\n WITH selected_graphs AS (\n SELECT DISTINCT\n manifest_graphs.graph_id\n FROM temp_direct_target_roots dtr\n CROSS JOIN manifest_graphs\n WHERE manifest_graphs.scan_id = ?\n AND manifest_graphs.digest = dtr.root_digest\n ),\n retained_tagged_manifests AS (\n SELECT DISTINCT\n m.digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs\n CROSS JOIN manifests m\n JOIN tags t\n ON t.scan_id = m.scan_id\n AND t.version_id = m.version_id\n AND t.is_digest_tag = 0\n WHERE manifest_graphs.scan_id = m.scan_id\n AND selected_graphs.graph_id = manifest_graphs.graph_id\n AND manifest_graphs.digest = m.digest\n AND m.scan_id = ?\n AND NOT EXISTS (\n SELECT 1\n FROM temp_direct_target_roots dtr\n WHERE dtr.root_digest = m.digest\n )\n ),\n retained_manifests AS (\n SELECT\n retained.digest\n FROM retained_tagged_manifests retained\n\n UNION\n\n SELECT\n mr.descendant_digest AS digest\n FROM retained_tagged_manifests retained\n CROSS JOIN manifest_reachability mr\n WHERE mr.scan_id = ?\n AND mr.ancestor_digest = retained.digest\n AND mr.min_distance > 0\n )\n SELECT DISTINCT\n dtr.root_digest\n FROM temp_direct_target_roots dtr\n WHERE dtr.root_manifest_kind = 'index_manifest'\n AND EXISTS (\n SELECT 1\n FROM manifest_edges me\n JOIN manifests child\n ON child.scan_id = ?\n AND child.digest = me.child_digest\n WHERE me.scan_id = ?\n AND me.parent_digest = dtr.root_digest\n AND me.edge_kind = 'referrer'\n AND child.manifest_kind = 'signature_manifest'\n )\n AND EXISTS (\n SELECT 1\n FROM manifest_edges me\n JOIN manifests child\n ON child.scan_id = ?\n AND child.digest = me.child_digest\n WHERE me.scan_id = ?\n AND me.parent_digest = dtr.root_digest\n AND me.edge_kind = 'image-child'\n AND child.manifest_kind <> 'signature_manifest'\n )\n AND NOT EXISTS (\n SELECT 1\n FROM manifest_edges me\n JOIN manifests child\n ON child.scan_id = ?\n AND child.digest = me.child_digest\n WHERE me.scan_id = ?\n AND me.parent_digest = dtr.root_digest\n AND me.edge_kind = 'image-child'\n AND child.manifest_kind <> 'signature_manifest'\n AND child.digest NOT IN (\n SELECT digest\n FROM retained_manifests\n )\n )\n";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export const _LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL = `
|
|
2
|
+
WITH selected_graphs AS (
|
|
3
|
+
SELECT DISTINCT
|
|
4
|
+
manifest_graphs.graph_id
|
|
5
|
+
FROM temp_direct_target_roots dtr
|
|
6
|
+
CROSS JOIN manifest_graphs
|
|
7
|
+
WHERE manifest_graphs.scan_id = ?
|
|
8
|
+
AND manifest_graphs.digest = dtr.root_digest
|
|
9
|
+
),
|
|
10
|
+
retained_tagged_manifests AS (
|
|
11
|
+
SELECT DISTINCT
|
|
12
|
+
m.digest
|
|
13
|
+
FROM selected_graphs
|
|
14
|
+
CROSS JOIN manifest_graphs
|
|
15
|
+
CROSS JOIN manifests m
|
|
16
|
+
JOIN tags t
|
|
17
|
+
ON t.scan_id = m.scan_id
|
|
18
|
+
AND t.version_id = m.version_id
|
|
19
|
+
AND t.is_digest_tag = 0
|
|
20
|
+
WHERE manifest_graphs.scan_id = m.scan_id
|
|
21
|
+
AND selected_graphs.graph_id = manifest_graphs.graph_id
|
|
22
|
+
AND manifest_graphs.digest = m.digest
|
|
23
|
+
AND m.scan_id = ?
|
|
24
|
+
AND NOT EXISTS (
|
|
25
|
+
SELECT 1
|
|
26
|
+
FROM temp_direct_target_roots dtr
|
|
27
|
+
WHERE dtr.root_digest = m.digest
|
|
28
|
+
)
|
|
29
|
+
),
|
|
30
|
+
retained_manifests AS (
|
|
31
|
+
SELECT
|
|
32
|
+
retained.digest
|
|
33
|
+
FROM retained_tagged_manifests retained
|
|
34
|
+
|
|
35
|
+
UNION
|
|
36
|
+
|
|
37
|
+
SELECT
|
|
38
|
+
mr.descendant_digest AS digest
|
|
39
|
+
FROM retained_tagged_manifests retained
|
|
40
|
+
CROSS JOIN manifest_reachability mr
|
|
41
|
+
WHERE mr.scan_id = ?
|
|
42
|
+
AND mr.ancestor_digest = retained.digest
|
|
43
|
+
AND mr.min_distance > 0
|
|
44
|
+
)
|
|
45
|
+
SELECT DISTINCT
|
|
46
|
+
dtr.root_digest
|
|
47
|
+
FROM temp_direct_target_roots dtr
|
|
48
|
+
WHERE dtr.root_manifest_kind = 'index_manifest'
|
|
49
|
+
AND EXISTS (
|
|
50
|
+
SELECT 1
|
|
51
|
+
FROM manifest_edges me
|
|
52
|
+
JOIN manifests child
|
|
53
|
+
ON child.scan_id = ?
|
|
54
|
+
AND child.digest = me.child_digest
|
|
55
|
+
WHERE me.scan_id = ?
|
|
56
|
+
AND me.parent_digest = dtr.root_digest
|
|
57
|
+
AND me.edge_kind = 'referrer'
|
|
58
|
+
AND child.manifest_kind = 'signature_manifest'
|
|
59
|
+
)
|
|
60
|
+
AND EXISTS (
|
|
61
|
+
SELECT 1
|
|
62
|
+
FROM manifest_edges me
|
|
63
|
+
JOIN manifests child
|
|
64
|
+
ON child.scan_id = ?
|
|
65
|
+
AND child.digest = me.child_digest
|
|
66
|
+
WHERE me.scan_id = ?
|
|
67
|
+
AND me.parent_digest = dtr.root_digest
|
|
68
|
+
AND me.edge_kind = 'image-child'
|
|
69
|
+
AND child.manifest_kind <> 'signature_manifest'
|
|
70
|
+
)
|
|
71
|
+
AND NOT EXISTS (
|
|
72
|
+
SELECT 1
|
|
73
|
+
FROM manifest_edges me
|
|
74
|
+
JOIN manifests child
|
|
75
|
+
ON child.scan_id = ?
|
|
76
|
+
AND child.digest = me.child_digest
|
|
77
|
+
WHERE me.scan_id = ?
|
|
78
|
+
AND me.parent_digest = dtr.root_digest
|
|
79
|
+
AND me.edge_kind = 'image-child'
|
|
80
|
+
AND child.manifest_kind <> 'signature_manifest'
|
|
81
|
+
AND child.digest NOT IN (
|
|
82
|
+
SELECT digest
|
|
83
|
+
FROM retained_manifests
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
`;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { mapBlockedRootRow, mapClosureManifestRow } from "./_planner-types.js";
|
|
2
|
+
import { _LIST_BLOCKED_ROOTS_SQL } from "./_planner-plan-artifacts-blocked-roots-sql.js";
|
|
3
|
+
import { _LIST_CLOSURE_MANIFESTS_SQL } from "./_planner-plan-artifacts-closure-sql.js";
|
|
4
|
+
import { _LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL } from "./_planner-plan-artifacts-supported-untag-only-sql.js";
|
|
2
5
|
export class PlannerPlanArtifacts {
|
|
3
6
|
#sql;
|
|
4
7
|
constructor(sql) {
|
|
@@ -10,7 +13,8 @@ export class PlannerPlanArtifacts {
|
|
|
10
13
|
return {
|
|
11
14
|
closureManifests: [],
|
|
12
15
|
blockedRoots: [],
|
|
13
|
-
fullyDeletableRoots: []
|
|
16
|
+
fullyDeletableRoots: [],
|
|
17
|
+
supportedUntagOnlyRootDigests: new Set()
|
|
14
18
|
};
|
|
15
19
|
}
|
|
16
20
|
return this.#withDirectTargetRootsTempTable(deleteRootCandidates, () => {
|
|
@@ -18,143 +22,37 @@ export class PlannerPlanArtifacts {
|
|
|
18
22
|
const blockedRoots = this.#listBlockedRoots(scanId);
|
|
19
23
|
const blockedVersionIds = new Set(blockedRoots.map((root) => root.blockedVersionId));
|
|
20
24
|
const fullyDeletableRoots = deleteRootCandidates.filter((root) => !blockedVersionIds.has(root.versionId));
|
|
25
|
+
const supportedUntagOnlyRootDigests = this.#listSupportedUntagOnlyRootDigests(scanId);
|
|
21
26
|
return {
|
|
22
27
|
closureManifests,
|
|
23
28
|
blockedRoots,
|
|
24
|
-
fullyDeletableRoots
|
|
29
|
+
fullyDeletableRoots,
|
|
30
|
+
supportedUntagOnlyRootDigests
|
|
25
31
|
};
|
|
26
32
|
});
|
|
27
33
|
}
|
|
34
|
+
#listSupportedUntagOnlyRootDigests(scanId) {
|
|
35
|
+
const rows = this.#sql.all(_LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL, [
|
|
36
|
+
scanId,
|
|
37
|
+
scanId,
|
|
38
|
+
scanId,
|
|
39
|
+
scanId,
|
|
40
|
+
scanId,
|
|
41
|
+
scanId,
|
|
42
|
+
scanId,
|
|
43
|
+
scanId,
|
|
44
|
+
scanId
|
|
45
|
+
]);
|
|
46
|
+
return new Set(rows.map((row) => row.root_digest));
|
|
47
|
+
}
|
|
28
48
|
#listClosureManifests(scanId) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
dtr.root_version_id AS source_version_id,
|
|
33
|
-
dtr.root_digest AS source_digest,
|
|
34
|
-
dtr.root_version_id AS member_version_id,
|
|
35
|
-
dtr.root_digest AS member_digest,
|
|
36
|
-
dtr.root_manifest_kind AS member_manifest_kind,
|
|
37
|
-
0 AS hops_from_root,
|
|
38
|
-
'root' AS member_role
|
|
39
|
-
FROM temp_direct_target_roots dtr
|
|
40
|
-
|
|
41
|
-
UNION ALL
|
|
42
|
-
|
|
43
|
-
SELECT
|
|
44
|
-
dtr.root_version_id AS source_version_id,
|
|
45
|
-
dtr.root_digest AS source_digest,
|
|
46
|
-
m.version_id AS member_version_id,
|
|
47
|
-
m.digest AS member_digest,
|
|
48
|
-
m.manifest_kind AS member_manifest_kind,
|
|
49
|
-
mr.min_distance AS hops_from_root,
|
|
50
|
-
'descendant' AS member_role
|
|
51
|
-
FROM temp_direct_target_roots dtr
|
|
52
|
-
JOIN manifest_reachability mr
|
|
53
|
-
ON mr.scan_id = ?
|
|
54
|
-
AND mr.ancestor_digest = dtr.root_digest
|
|
55
|
-
AND mr.min_distance > 0
|
|
56
|
-
JOIN manifests m
|
|
57
|
-
ON m.scan_id = ?
|
|
58
|
-
AND m.digest = mr.descendant_digest
|
|
59
|
-
)
|
|
60
|
-
SELECT
|
|
61
|
-
source_version_id,
|
|
62
|
-
source_digest,
|
|
63
|
-
member_version_id,
|
|
64
|
-
member_digest,
|
|
65
|
-
member_manifest_kind,
|
|
66
|
-
hops_from_root,
|
|
67
|
-
member_role
|
|
68
|
-
FROM direct_target_closure
|
|
69
|
-
ORDER BY source_digest, hops_from_root, member_digest
|
|
70
|
-
`;
|
|
71
|
-
return this.#sql.all(sql, [scanId, scanId]).map(mapClosureManifestRow);
|
|
49
|
+
return this.#sql
|
|
50
|
+
.all(_LIST_CLOSURE_MANIFESTS_SQL, [scanId, scanId, scanId, scanId, scanId, scanId, scanId, scanId, scanId, scanId])
|
|
51
|
+
.map(mapClosureManifestRow);
|
|
72
52
|
}
|
|
73
53
|
#listBlockedRoots(scanId) {
|
|
74
|
-
const sql = `
|
|
75
|
-
WITH retained_roots AS (
|
|
76
|
-
SELECT
|
|
77
|
-
m.version_id AS root_version_id,
|
|
78
|
-
m.digest AS root_digest
|
|
79
|
-
FROM manifests m
|
|
80
|
-
WHERE m.scan_id = ?
|
|
81
|
-
AND NOT EXISTS (
|
|
82
|
-
SELECT 1
|
|
83
|
-
FROM manifest_reachability mr
|
|
84
|
-
WHERE mr.scan_id = m.scan_id
|
|
85
|
-
AND mr.descendant_digest = m.digest
|
|
86
|
-
AND mr.min_distance > 0
|
|
87
|
-
)
|
|
88
|
-
AND NOT EXISTS (
|
|
89
|
-
SELECT 1
|
|
90
|
-
FROM temp_direct_target_roots dtr
|
|
91
|
-
WHERE dtr.root_digest = m.digest
|
|
92
|
-
)
|
|
93
|
-
),
|
|
94
|
-
direct_target_closure AS (
|
|
95
|
-
SELECT
|
|
96
|
-
dtr.root_version_id AS root_version_id,
|
|
97
|
-
dtr.root_digest AS root_digest,
|
|
98
|
-
dtr.root_manifest_kind AS member_manifest_kind,
|
|
99
|
-
dtr.root_digest AS member_digest,
|
|
100
|
-
0 AS hops_from_root
|
|
101
|
-
FROM temp_direct_target_roots dtr
|
|
102
|
-
|
|
103
|
-
UNION ALL
|
|
104
|
-
|
|
105
|
-
SELECT
|
|
106
|
-
dtr.root_version_id AS root_version_id,
|
|
107
|
-
dtr.root_digest AS root_digest,
|
|
108
|
-
m.manifest_kind AS member_manifest_kind,
|
|
109
|
-
m.digest AS member_digest,
|
|
110
|
-
mr.min_distance AS hops_from_root
|
|
111
|
-
FROM temp_direct_target_roots dtr
|
|
112
|
-
JOIN manifest_reachability mr
|
|
113
|
-
ON mr.scan_id = ?
|
|
114
|
-
AND mr.ancestor_digest = dtr.root_digest
|
|
115
|
-
AND mr.min_distance > 0
|
|
116
|
-
JOIN manifests m
|
|
117
|
-
ON m.scan_id = ?
|
|
118
|
-
AND m.digest = mr.descendant_digest
|
|
119
|
-
),
|
|
120
|
-
ranked_blocks AS (
|
|
121
|
-
SELECT
|
|
122
|
-
dtc.root_version_id AS blocked_version_id,
|
|
123
|
-
dtc.root_digest AS blocked_digest,
|
|
124
|
-
rr.root_version_id AS blocking_version_id,
|
|
125
|
-
rr.root_digest AS blocking_digest,
|
|
126
|
-
dtc.member_digest AS overlap_digest,
|
|
127
|
-
dtc.member_manifest_kind AS overlap_manifest_kind,
|
|
128
|
-
'overlap-with-retained-root' AS block_reason,
|
|
129
|
-
ROW_NUMBER() OVER (
|
|
130
|
-
PARTITION BY dtc.root_digest, rr.root_digest
|
|
131
|
-
ORDER BY
|
|
132
|
-
dtc.hops_from_root,
|
|
133
|
-
retained_overlap.min_distance,
|
|
134
|
-
dtc.member_digest
|
|
135
|
-
) AS rn
|
|
136
|
-
FROM direct_target_closure dtc
|
|
137
|
-
JOIN retained_roots rr
|
|
138
|
-
ON rr.root_digest <> dtc.root_digest
|
|
139
|
-
JOIN manifest_reachability retained_overlap
|
|
140
|
-
ON retained_overlap.scan_id = ?
|
|
141
|
-
AND retained_overlap.ancestor_digest = rr.root_digest
|
|
142
|
-
AND retained_overlap.descendant_digest = dtc.member_digest
|
|
143
|
-
)
|
|
144
|
-
SELECT
|
|
145
|
-
blocked_version_id,
|
|
146
|
-
blocked_digest,
|
|
147
|
-
blocking_version_id,
|
|
148
|
-
blocking_digest,
|
|
149
|
-
overlap_digest,
|
|
150
|
-
overlap_manifest_kind,
|
|
151
|
-
block_reason
|
|
152
|
-
FROM ranked_blocks
|
|
153
|
-
WHERE rn = 1
|
|
154
|
-
ORDER BY blocked_digest, blocking_digest, overlap_digest
|
|
155
|
-
`;
|
|
156
54
|
return this.#sql
|
|
157
|
-
.all(
|
|
55
|
+
.all(_LIST_BLOCKED_ROOTS_SQL, [scanId, scanId, scanId])
|
|
158
56
|
.map(mapBlockedRootRow);
|
|
159
57
|
}
|
|
160
58
|
#withDirectTargetRootsTempTable(directTargetRoots, callback) {
|
|
@@ -14,16 +14,24 @@ export class PlannerSql {
|
|
|
14
14
|
}
|
|
15
15
|
exec(sql, params = []) {
|
|
16
16
|
this.#traceSql(sql, params);
|
|
17
|
+
const startedAt = Date.now();
|
|
17
18
|
this.#database.prepare(sql).run(...params);
|
|
19
|
+
this.#logger.trace(`SQL exec completed in ${Date.now() - startedAt} ms`);
|
|
18
20
|
}
|
|
19
21
|
get(sql, params) {
|
|
22
|
+
this.#debugSql(sql, params);
|
|
20
23
|
this.#traceSql(sql, params);
|
|
21
|
-
|
|
24
|
+
const startedAt = Date.now();
|
|
25
|
+
const row = this.#database.prepare(sql).get(...params);
|
|
26
|
+
this.#logger.debug(`SQL returned ${row === undefined ? "0" : "1"} row(s) in ${Date.now() - startedAt} ms`);
|
|
27
|
+
return row;
|
|
22
28
|
}
|
|
23
29
|
all(sql, params) {
|
|
30
|
+
this.#debugSql(sql, params);
|
|
24
31
|
this.#traceSql(sql, params);
|
|
32
|
+
const startedAt = Date.now();
|
|
25
33
|
const rows = this.#database.prepare(sql).all(...params);
|
|
26
|
-
this.#logger.debug(`SQL returned ${rows.length} row(s)`);
|
|
34
|
+
this.#logger.debug(`SQL returned ${rows.length} row(s) in ${Date.now() - startedAt} ms`);
|
|
27
35
|
return rows;
|
|
28
36
|
}
|
|
29
37
|
traceSql(sql, params) {
|
|
@@ -32,4 +40,7 @@ export class PlannerSql {
|
|
|
32
40
|
#traceSql(sql, params) {
|
|
33
41
|
this.#logger.trace(`SQL:\n${sql.trim()}\nPARAMS: ${JSON.stringify(params)}`);
|
|
34
42
|
}
|
|
43
|
+
#debugSql(sql, params) {
|
|
44
|
+
this.#logger.debug(`SQL:\n${sql.trim()}\nPARAMS: ${JSON.stringify(params)}`);
|
|
45
|
+
}
|
|
35
46
|
}
|
|
@@ -48,6 +48,7 @@ export declare const DeletePlanValidationStatuses: {
|
|
|
48
48
|
export type DeletePlanValidationStatus = (typeof DeletePlanValidationStatuses)[keyof typeof DeletePlanValidationStatuses];
|
|
49
49
|
export declare const DeletePlanValidationReasonCodes: {
|
|
50
50
|
readonly untagOnlyPartialTagMatch: "untag-only-partial-tag-match";
|
|
51
|
+
readonly untagOnlyRetainedManifest: "untag-only-retained-manifest";
|
|
51
52
|
readonly fullyDeletableNoRetainedOverlap: "fully-deletable-no-retained-overlap";
|
|
52
53
|
readonly blockedOverlapWithRetainedRoot: "blocked-overlap-with-retained-root";
|
|
53
54
|
};
|
|
@@ -106,6 +107,7 @@ export interface PlanArtifacts {
|
|
|
106
107
|
closureManifests: DeletePlanClosureManifest[];
|
|
107
108
|
blockedRoots: DeletePlanBlockedRoot[];
|
|
108
109
|
fullyDeletableRoots: DeletePlanRoot[];
|
|
110
|
+
supportedUntagOnlyRootDigests: Set<string>;
|
|
109
111
|
}
|
|
110
112
|
export interface DeletePlan {
|
|
111
113
|
owner: string;
|