]> git.ipfire.org Git - thirdparty/git.git/blame - reftable/merged.c
reftable/merged: make subiters own their records
[thirdparty/git.git] / reftable / merged.c
CommitLineData
1ae2b8cd
HWN
1/*
2Copyright 2020 Google LLC
3
4Use of this source code is governed by a BSD-style
5license that can be found in the LICENSE file or at
6https://developers.google.com/open-source/licenses/bsd
7*/
8
9#include "merged.h"
10
11#include "constants.h"
12#include "iter.h"
13#include "pq.h"
1ae2b8cd
HWN
14#include "record.h"
15#include "generic.h"
16#include "reftable-merged.h"
17#include "reftable-error.h"
18#include "system.h"
19
bb2d6be4
PS
20struct merged_subiter {
21 struct reftable_iterator iter;
22 struct reftable_record rec;
23};
24
48929d2e 25struct merged_iter {
bb2d6be4 26 struct merged_subiter *subiters;
aad8ad6f 27 struct merged_iter_pqueue pq;
48929d2e
PS
28 uint32_t hash_id;
29 size_t stack_len;
30 uint8_t typ;
31 int suppress_deletions;
aad8ad6f 32 ssize_t advance_index;
48929d2e
PS
33};
34
1ae2b8cd
HWN
35static int merged_iter_init(struct merged_iter *mi)
36{
62d3c8e8
PS
37 for (size_t i = 0; i < mi->stack_len; i++) {
38 struct pq_entry e = {
62d3c8e8 39 .index = i,
bb2d6be4 40 .rec = &mi->subiters[i].rec,
62d3c8e8
PS
41 };
42 int err;
43
bb2d6be4
PS
44 reftable_record_init(&mi->subiters[i].rec, mi->typ);
45 err = iterator_next(&mi->subiters[i].iter,
46 &mi->subiters[i].rec);
62d3c8e8 47 if (err < 0)
1ae2b8cd 48 return err;
1ae2b8cd 49 if (err > 0) {
bb2d6be4
PS
50 reftable_iterator_destroy(&mi->subiters[i].iter);
51 reftable_record_release(&mi->subiters[i].rec);
62d3c8e8 52 continue;
1ae2b8cd 53 }
62d3c8e8
PS
54
55 merged_iter_pqueue_add(&mi->pq, &e);
1ae2b8cd
HWN
56 }
57
58 return 0;
59}
60
61static void merged_iter_close(void *p)
62{
63 struct merged_iter *mi = p;
81879123 64
1ae2b8cd 65 merged_iter_pqueue_release(&mi->pq);
bb2d6be4
PS
66 for (size_t i = 0; i < mi->stack_len; i++) {
67 reftable_iterator_destroy(&mi->subiters[i].iter);
68 reftable_record_release(&mi->subiters[i].rec);
69 }
70 reftable_free(mi->subiters);
1ae2b8cd
HWN
71}
72
73static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
74 size_t idx)
75{
1ae2b8cd 76 struct pq_entry e = {
1ae2b8cd 77 .index = idx,
bb2d6be4 78 .rec = &mi->subiters[idx].rec,
1ae2b8cd 79 };
3ddef475
PS
80 int err;
81
bb2d6be4 82 err = iterator_next(&mi->subiters[idx].iter, &mi->subiters[idx].rec);
1ae2b8cd
HWN
83 if (err < 0)
84 return err;
1ae2b8cd 85 if (err > 0) {
bb2d6be4
PS
86 reftable_iterator_destroy(&mi->subiters[idx].iter);
87 reftable_record_release(&mi->subiters[idx].rec);
1ae2b8cd
HWN
88 return 0;
89 }
90
c18eecbe 91 merged_iter_pqueue_add(&mi->pq, &e);
1ae2b8cd
HWN
92 return 0;
93}
94
95static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)
96{
bb2d6be4 97 if (iterator_is_null(&mi->subiters[idx].iter))
1ae2b8cd
HWN
98 return 0;
99 return merged_iter_advance_nonnull_subiter(mi, idx);
100}
101
102static int merged_iter_next_entry(struct merged_iter *mi,
103 struct reftable_record *rec)
104{
1ae2b8cd
HWN
105 struct pq_entry entry = { 0 };
106 int err = 0;
107
aad8ad6f
PS
108 if (mi->advance_index >= 0) {
109 err = merged_iter_advance_subiter(mi, mi->advance_index);
110 if (err < 0)
111 return err;
112 mi->advance_index = -1;
113 }
114
1ae2b8cd
HWN
115 if (merged_iter_pqueue_is_empty(mi->pq))
116 return 1;
117
118 entry = merged_iter_pqueue_remove(&mi->pq);
1ae2b8cd
HWN
119
120 /*
121 One can also use reftable as datacenter-local storage, where the ref
122 database is maintained in globally consistent database (eg.
123 CockroachDB or Spanner). In this scenario, replication delays together
124 with compaction may cause newer tables to contain older entries. In
125 such a deployment, the loop below must be changed to collect all
126 entries for the same key, and return new the newest one.
127 */
1ae2b8cd
HWN
128 while (!merged_iter_pqueue_is_empty(mi->pq)) {
129 struct pq_entry top = merged_iter_pqueue_top(mi->pq);
a96e9a20 130 int cmp;
1ae2b8cd 131
bb2d6be4 132 cmp = reftable_record_cmp(top.rec, entry.rec);
829231dc 133 if (cmp > 0)
1ae2b8cd 134 break;
1ae2b8cd
HWN
135
136 merged_iter_pqueue_remove(&mi->pq);
137 err = merged_iter_advance_subiter(mi, top.index);
829231dc 138 if (err < 0)
bb2d6be4 139 return err;
1ae2b8cd
HWN
140 }
141
aad8ad6f 142 mi->advance_index = entry.index;
bb2d6be4
PS
143 SWAP(*rec, *entry.rec);
144 return 0;
1ae2b8cd
HWN
145}
146
147static int merged_iter_next(struct merged_iter *mi, struct reftable_record *rec)
148{
149 while (1) {
150 int err = merged_iter_next_entry(mi, rec);
151 if (err == 0 && mi->suppress_deletions &&
152 reftable_record_is_deletion(rec)) {
153 continue;
154 }
155
156 return err;
157 }
158}
159
160static int merged_iter_next_void(void *p, struct reftable_record *rec)
161{
162 struct merged_iter *mi = p;
aad8ad6f 163 if (merged_iter_pqueue_is_empty(mi->pq) && mi->advance_index < 0)
1ae2b8cd 164 return 1;
1ae2b8cd
HWN
165 return merged_iter_next(mi, rec);
166}
167
168static struct reftable_iterator_vtable merged_iter_vtable = {
169 .next = &merged_iter_next_void,
170 .close = &merged_iter_close,
171};
172
173static void iterator_from_merged_iter(struct reftable_iterator *it,
174 struct merged_iter *mi)
175{
176 assert(!it->ops);
177 it->iter_arg = mi;
178 it->ops = &merged_iter_vtable;
179}
180
181int reftable_new_merged_table(struct reftable_merged_table **dest,
81879123 182 struct reftable_table *stack, size_t n,
1ae2b8cd
HWN
183 uint32_t hash_id)
184{
185 struct reftable_merged_table *m = NULL;
186 uint64_t last_max = 0;
187 uint64_t first_min = 0;
81879123
PS
188
189 for (size_t i = 0; i < n; i++) {
1ae2b8cd
HWN
190 uint64_t min = reftable_table_min_update_index(&stack[i]);
191 uint64_t max = reftable_table_max_update_index(&stack[i]);
192
193 if (reftable_table_hash_id(&stack[i]) != hash_id) {
194 return REFTABLE_FORMAT_ERROR;
195 }
196 if (i == 0 || min < first_min) {
197 first_min = min;
198 }
199 if (i == 0 || max > last_max) {
200 last_max = max;
201 }
202 }
203
b4ff12c8 204 REFTABLE_CALLOC_ARRAY(m, 1);
1ae2b8cd
HWN
205 m->stack = stack;
206 m->stack_len = n;
207 m->min = first_min;
208 m->max = last_max;
209 m->hash_id = hash_id;
210 *dest = m;
211 return 0;
212}
213
214/* clears the list of subtable, without affecting the readers themselves. */
215void merged_table_release(struct reftable_merged_table *mt)
216{
217 FREE_AND_NULL(mt->stack);
218 mt->stack_len = 0;
219}
220
221void reftable_merged_table_free(struct reftable_merged_table *mt)
222{
223 if (!mt) {
224 return;
225 }
226 merged_table_release(mt);
227 reftable_free(mt);
228}
229
230uint64_t
231reftable_merged_table_max_update_index(struct reftable_merged_table *mt)
232{
233 return mt->max;
234}
235
236uint64_t
237reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
238{
239 return mt->min;
240}
241
242static int reftable_table_seek_record(struct reftable_table *tab,
243 struct reftable_iterator *it,
244 struct reftable_record *rec)
245{
246 return tab->ops->seek_record(tab->table_arg, it, rec);
247}
248
249static int merged_table_seek_record(struct reftable_merged_table *mt,
250 struct reftable_iterator *it,
251 struct reftable_record *rec)
252{
1ae2b8cd 253 struct merged_iter merged = {
1ae2b8cd
HWN
254 .typ = reftable_record_type(rec),
255 .hash_id = mt->hash_id,
256 .suppress_deletions = mt->suppress_deletions,
aad8ad6f 257 .advance_index = -1,
1ae2b8cd 258 };
59f302ca
PS
259 struct merged_iter *p;
260 int err;
261
bb2d6be4 262 REFTABLE_CALLOC_ARRAY(merged.subiters, mt->stack_len);
59f302ca
PS
263 for (size_t i = 0; i < mt->stack_len; i++) {
264 err = reftable_table_seek_record(&mt->stack[i],
bb2d6be4 265 &merged.subiters[merged.stack_len].iter, rec);
59f302ca
PS
266 if (err < 0)
267 goto out;
268 if (!err)
269 merged.stack_len++;
1ae2b8cd
HWN
270 }
271
1ae2b8cd 272 err = merged_iter_init(&merged);
59f302ca
PS
273 if (err < 0)
274 goto out;
275
276 p = reftable_malloc(sizeof(struct merged_iter));
277 *p = merged;
278 iterator_from_merged_iter(it, p);
279
280out:
281 if (err < 0)
1ae2b8cd 282 merged_iter_close(&merged);
59f302ca 283 return err;
1ae2b8cd
HWN
284}
285
286int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
287 struct reftable_iterator *it,
288 const char *name)
289{
66c0daba
HWN
290 struct reftable_record rec = {
291 .type = BLOCK_TYPE_REF,
292 .u.ref = {
293 .refname = (char *)name,
294 },
1ae2b8cd 295 };
1ae2b8cd
HWN
296 return merged_table_seek_record(mt, it, &rec);
297}
298
299int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
300 struct reftable_iterator *it,
301 const char *name, uint64_t update_index)
302{
66c0daba
HWN
303 struct reftable_record rec = { .type = BLOCK_TYPE_LOG,
304 .u.log = {
305 .refname = (char *)name,
306 .update_index = update_index,
307 } };
1ae2b8cd
HWN
308 return merged_table_seek_record(mt, it, &rec);
309}
310
311int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
312 struct reftable_iterator *it,
313 const char *name)
314{
315 uint64_t max = ~((uint64_t)0);
316 return reftable_merged_table_seek_log_at(mt, it, name, max);
317}
318
319uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
320{
321 return mt->hash_id;
322}
323
324static int reftable_merged_table_seek_void(void *tab,
325 struct reftable_iterator *it,
326 struct reftable_record *rec)
327{
328 return merged_table_seek_record(tab, it, rec);
329}
330
331static uint32_t reftable_merged_table_hash_id_void(void *tab)
332{
333 return reftable_merged_table_hash_id(tab);
334}
335
336static uint64_t reftable_merged_table_min_update_index_void(void *tab)
337{
338 return reftable_merged_table_min_update_index(tab);
339}
340
341static uint64_t reftable_merged_table_max_update_index_void(void *tab)
342{
343 return reftable_merged_table_max_update_index(tab);
344}
345
346static struct reftable_table_vtable merged_table_vtable = {
347 .seek_record = reftable_merged_table_seek_void,
348 .hash_id = reftable_merged_table_hash_id_void,
349 .min_update_index = reftable_merged_table_min_update_index_void,
350 .max_update_index = reftable_merged_table_max_update_index_void,
351};
352
353void reftable_table_from_merged_table(struct reftable_table *tab,
354 struct reftable_merged_table *merged)
355{
356 assert(!tab->ops);
357 tab->ops = &merged_table_vtable;
358 tab->table_arg = merged;
359}