GCC Code Coverage Report


Directory: src/
File: src/pg/explain.c
Date: 2026-09-30 11:11:31
Exec Total Coverage
Lines: 43 47 91.5%
Functions: 2 2 100.0%
Branches: 12 18 66.7%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2026 Tiger Data, Inc.
3 * Licensed under the PostgreSQL License. See LICENSE for details.
4 *
5 * explain.c - EXPLAIN ANALYZE output for prism scans
6 *
7 * Injects scan stats into EXPLAIN (ANALYZE, VERBOSE) output via
8 * PG's explain_per_node_hook. Shows centroid search, posting scan,
9 * and rerank statistics for prism index scans.
10 */
11
12 #include <postgres.h>
13
14 #include "vs_config.h"
15
16 #include <commands/defrem.h>
17 #include <commands/explain.h>
18 #include <commands/explain_format.h>
19 #include <commands/explain_state.h>
20 #include <nodes/execnodes.h>
21 #include <utils/lsyscache.h>
22 #include <utils/rel.h>
23
24 #include "core/platform.h"
25 #include "explain.h"
26 #include "scan.h"
27
28 static explain_per_node_hook_type prev_hook = NULL;
29
30 static void
31 196 prism_explain_hook(
32 PlanState *planstate,
33 List *ancestors,
34 const char *relationship,
35 const char *plan_name,
36 ExplainState *es)
37 {
38
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 196 times.
196 if (prev_hook)
39 ✗ prev_hook(planstate, ancestors, relationship, plan_name, es);
40
41
4/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 59 times.
✓ Branch 3 taken 7 times.
196 if (!es->analyze || !es->verbose)
42 return;
43
44
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 39 times.
59 if (!IsA(planstate, IndexScanState))
45 return;
46
47 20 IndexScanState *iss = (IndexScanState *)planstate;
48 20 IndexScanDesc scan = iss->iss_ScanDesc;
49
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (scan == NULL)
50 return;
51
52 20 Relation rel = scan->indexRelation;
53 20 char *am_name = get_am_name(rel->rd_rel->relam);
54
2/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
20 if (am_name == NULL || strcmp(am_name, VS_AM_NAME) != 0)
55 {
56 ✗ if (am_name != NULL)
57 ✗ pfree(am_name);
58 ✗ return;
59 }
60 20 pfree(am_name);
61
62 20 const PrismScanStats *stats = prism_scan_get_stats(scan);
63
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (stats == NULL)
64 return;
65
66 20 ExplainOpenGroup("Prism", "Prism", true, es);
67
68 20 ExplainPropertyInteger("Top-K", NULL, stats->top_k, es);
69 20 ExplainPropertyInteger(
70 20 "Posting Lists Scanned", NULL, stats->clusters_scanned, es);
71 20 ExplainPropertyInteger(
72 20 "Centroid Pages Read", NULL, stats->centroid_pages_read, es);
73 20 ExplainPropertyInteger(
74 20 "Posting Pages Read", NULL, stats->posting_pages_read, es);
75 20 ExplainPropertyInteger(
76 "Posting Pages Scanned",
77 NULL,
78 20 stats->posting_pages_read - stats->posting_pages_skipped,
79 es);
80 20 ExplainPropertyInteger(
81 "Posting Dead Pages Skipped",
82 NULL,
83 20 stats->posting_pages_skipped,
84 es);
85 20 ExplainPropertyInteger(
86 "Posting Entries Scanned",
87 NULL,
88 20 stats->posting_entries_scanned,
89 es);
90 20 ExplainPropertyInteger(
91 20 "Rerank Candidates", NULL, stats->rerank_candidates, es);
92 20 ExplainPropertyInteger("Rerank Results", NULL, stats->rerank_results, es);
93 20 ExplainPropertyInteger("Storage Reads", NULL, stats->storage_reads, es);
94
95 /* Per-phase wall time (ms). Emitted only under EXPLAIN (ANALYZE,
96 * VERBOSE, TIMING ON) so regression output stays stable without it. */
97
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (es->timing)
98 {
99 20 ExplainPropertyFloat(
100 "Centroid Search Time",
101 "ms",
102 20 (double)stats->centroid_ns / VS_NS_PER_MS,
103 3,
104 es);
105 20 ExplainPropertyFloat(
106 "Posting Scan Time",
107 "ms",
108 20 (double)stats->posting_ns / VS_NS_PER_MS,
109 3,
110 es);
111 20 ExplainPropertyFloat(
112 "Rerank Time",
113 "ms",
114 20 (double)stats->rerank_ns / VS_NS_PER_MS,
115 3,
116 es);
117 }
118
119 20 ExplainCloseGroup("Prism", "Prism", true, es);
120 }
121
122 void
123 255 prism_explain_init(void)
124 {
125 255 prev_hook = explain_per_node_hook;
126 255 explain_per_node_hook = prism_explain_hook;
127 255 }
128