GCC Code Coverage Report


Directory: src/
File: src/index/posting_page.c
Date: 2026-10-23 09:17:32
Exec Total Coverage
Lines: 83 88 94.3%
Functions: 7 7 100.0%
Branches: 17 26 65.4%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2026 Tiger Data, Inc.
3 * Licensed under the PostgreSQL License. See LICENSE for details.
4 *
5 * posting_page.c - Posting list page operations
6 */
7
8 #include "vs_config.h"
9
10 #include "core/log.h"
11 #include "index/posting_page.h"
12
13 #ifndef VS_STANDALONE
14 #include <miscadmin.h> /* CHECK_FOR_INTERRUPTS */
15 #endif
16
17 void
18 4602 prism_posting_check_count(
19 BlockNumber blkno, const PrismPostingPageOpaque *op, Dimension dim)
20 {
21 4602 uint32_t cap = prism_posting_page_cap(op, dim);
22
23
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4602 times.
4602 if (op->entry_count > cap)
24 ✗ vs_error(
25 VS_EXTENSION_NAME
26 ": posting page %u has an invalid entry count (%u > %u); "
27 "the index may be corrupted -- REINDEX it",
28 blkno,
29 (unsigned)op->entry_count,
30 cap);
31 4602 }
32
33 /* ----------------------------------------------------------------
34 * Paged mode (BLCKSZ pages with PG header + opaque)
35 * ---------------------------------------------------------------- */
36
37 void
38 32811 prism_posting_page_init(
39 Page page, uint32_t cluster_id, Dimension dim, uint16_t flags)
40 {
41 32811 PageInit(page, BLCKSZ, sizeof(PrismPostingPageOpaque));
42
43 /* First pages have reduced capacity due to pt_centroid */
44 65622 uint32_t max = (flags & PRISM_POSTING_PAGE_FIRST)
45 12010 ? prism_posting_max_entries_first(dim)
46
2/2
✓ Branch 0 taken 12010 times.
✓ Branch 1 taken 20801 times.
32811 : prism_posting_max_entries(dim);
47 32811 PrismPostingPageOpaque *opaque = prism_posting_opaque(page);
48 32811 opaque->next_blkno = InvalidBlockNumber;
49 32811 opaque->cluster_id = cluster_id;
50 32811 opaque->entry_count = 0;
51 32811 opaque->flags = flags;
52 32811 opaque->page_id = PRISM_POSTING_PAGE_ID;
53 32811 opaque->max_entries = (uint16_t)max;
54 /* Head metadata starts "not computed"; the first insert fills it (the
55 * fields are only consulted on the FIRST page). */
56 32811 opaque->live_count = 0;
57 32811 opaque->tail_blkno = InvalidBlockNumber;
58 32811 }
59
60 bool
61 260807 prism_posting_page_add(
62 Page page,
63 Dimension dim,
64 ItemPointerData tid,
65 float f_add,
66 float f_rescale,
67 float f_error,
68 const uint8_t *bits,
69 uint8_t entry_flags)
70 {
71
2/2
✓ Branch 1 taken 60769 times.
✓ Branch 2 taken 200038 times.
260807 if (!prism_posting_page_has_room(page))
72 2 return false;
73
74 260805 PrismPostingPageOpaque *opaque = prism_posting_opaque(page);
75 260805 uint32_t i = opaque->entry_count;
76 260805 char *content = prism_posting_page_content(page, dim);
77
78 /* Write the entry header (meta + factors) + bits in one contiguous
79 * block at content + i * entry_size. */
80
1/2
✓ Branch 0 taken 60767 times.
✗ Branch 1 not taken.
260805 PrismPostingEntryHeader *hdr = prism_posting_entry_at(content, i, dim);
81 260805 hdr->meta.tid = tid;
82 260805 hdr->meta.flags = entry_flags;
83 260805 hdr->meta.reserved = 0;
84 260805 hdr->f_add = f_add;
85 260805 hdr->f_rescale = f_rescale;
86 260805 hdr->f_error = f_error;
87
1/2
✓ Branch 0 taken 60767 times.
✗ Branch 1 not taken.
260805 memcpy(hdr->bits, bits, VS_RABITQ_BYTES(dim));
88
89 260805 opaque->entry_count = i + 1;
90
91 /* A live entry means the page is no longer wholly dead, so drop the
92 * scan-side shortcut -- see PRISM_POSTING_PAGE_TOMBSTONED. */
93
1/2
✓ Branch 0 taken 260805 times.
✗ Branch 1 not taken.
260805 if ((entry_flags & PRISM_POSTING_FLAG_DELETED) == 0)
94 260805 opaque->flags &= (uint16_t)~PRISM_POSTING_PAGE_TOMBSTONED;
95
96 200038 return true;
97 }
98
99 /* ----------------------------------------------------------------
100 * Chain walks
101 * ---------------------------------------------------------------- */
102
103 void
104 3700 prism_posting_chain_walk(
105 VsStorage *storage,
106 BlockNumber head,
107 PrismPostingChainCb cb,
108 void *state)
109 {
110 3700 PrismPostingChainPos pos = {.storage = storage, .first = true};
111 3700 BlockNumber blk = head;
112
113
2/2
✓ Branch 0 taken 4418 times.
✓ Branch 1 taken 3700 times.
8118 while (blk != InvalidBlockNumber)
114 {
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3612 times.
3612 CHECK_FOR_INTERRUPTS();
116
117 4418 pos.blkno = blk;
118 4418 pos.page = vs_storage_read_page(storage, blk);
119
120 /*
121 * Reading next_blkno means casting the special area, so establish
122 * that it is a posting page first -- the same guard every other
123 * page read applies. A chain that leaves posting pages is a
124 * corrupt chain, and ends the walk rather than being followed.
125 */
126
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4418 times.
4418 if (!prism_page_is_posting(pos.page))
127 {
128 ✗ prism_posting_chain_release(&pos);
129 ✗ return;
130 }
131
132 4418 pos.next = prism_posting_opaque(pos.page)->next_blkno;
133
134 4418 bool keep_going = cb(&pos, state);
135
136 /* No-op when the callback released it already. */
137 4418 prism_posting_chain_release(&pos);
138
139
2/2
✓ Branch 0 taken 3612 times.
✓ Branch 1 taken 806 times.
4418 if (!keep_going)
140 ✗ return;
141
142 4418 pos.first = false;
143 4418 blk = pos.next;
144 }
145 }
146
147 void
148 166 prism_posting_chain_mutate(
149 VsStorage *storage,
150 BlockNumber head,
151 PrismPostingChainMutateCb cb,
152 void *state)
153 {
154 166 BlockNumber blk = head;
155
156
2/2
✓ Branch 0 taken 425 times.
✓ Branch 1 taken 166 times.
591 while (blk != InvalidBlockNumber)
157 {
158 425 Page page = vs_storage_write_page(storage, blk);
159 425 PrismPostingPageOpaque *op = prism_posting_opaque(page);
160 425 BlockNumber next = op->next_blkno;
161
162 425 cb(op, state);
163
164 425 vs_storage_commit_page(storage, blk);
165 425 blk = next;
166 }
167 166 }
168
169 /* ----------------------------------------------------------------
170 * Flat mode (one buffer per cluster, custom header)
171 * ---------------------------------------------------------------- */
172
173 void
174 168 prism_posting_flat_init(char *buf, uint32_t max_entries, uint32_t cluster_id)
175 {
176 168 PrismFlatPostingHeader *hdr = prism_flat_posting_header(buf);
177 168 hdr->max_entries = max_entries;
178 168 hdr->entry_count = 0;
179 168 hdr->cluster_id = cluster_id;
180 168 hdr->_pad = 0;
181 168 }
182
183 bool
184 11000 prism_posting_flat_add(
185 char *buf,
186 Dimension dim,
187 ItemPointerData tid,
188 float f_add,
189 float f_rescale,
190 float f_error,
191 const uint8_t *bits,
192 uint8_t entry_flags)
193 {
194 11000 PrismFlatPostingHeader *flat_hdr = prism_flat_posting_header(buf);
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11000 times.
11000 if (flat_hdr->entry_count >= flat_hdr->max_entries)
196 ✗ return false;
197
198 11000 uint32_t i = flat_hdr->entry_count;
199 11000 char *content = prism_flat_posting_content(buf);
200
201 11000 PrismPostingEntryHeader *hdr = prism_posting_entry_at(content, i, dim);
202 11000 hdr->meta.tid = tid;
203 11000 hdr->meta.flags = entry_flags;
204 11000 hdr->meta.reserved = 0;
205 11000 hdr->f_add = f_add;
206 11000 hdr->f_rescale = f_rescale;
207 11000 hdr->f_error = f_error;
208 11000 memcpy(hdr->bits, bits, VS_RABITQ_BYTES(dim));
209
210 11000 flat_hdr->entry_count = i + 1;
211 11000 return true;
212 }
213