reftable/reader: make table iterator reseekable
In 67ce50ba26 (Merge branch 'ps/reftable-reusable-iterator', 2024-05-30) we have refactored the interface of reftable iterators such that they can be reused in theory. This patch series only landed the required changes on the interface level, but didn't yet implement the actual logic to make iterators reusable. As it turns out almost all of the infrastructure already does support re-seeking. The only exception is the table iterator, which does not reset its `is_finished` bit. Do so and add a couple of tests that verify that we can re-seek iterators. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Patrick Steinhardt committed
Sep 16, 2024 at 10:50 UTC
0a148a8eda7da6f7ff039ff2ea62b143ceb57f2f
4 files changed
+174
Makefile
+1
@@ -1346,6 +1346,7 @@ UNIT_TEST_PROGRAMS += t-reftable-basics
1346
UNIT_TEST_PROGRAMS += t-reftable-block
1347
UNIT_TEST_PROGRAMS += t-reftable-merged
1348
UNIT_TEST_PROGRAMS += t-reftable-pq
1349
+UNIT_TEST_PROGRAMS += t-reftable-reader
1350
UNIT_TEST_PROGRAMS += t-reftable-readwrite
1351
UNIT_TEST_PROGRAMS += t-reftable-record
1352
UNIT_TEST_PROGRAMS += t-reftable-stack
reftable/reader.c
+1
@@ -328,6 +328,7 @@ static int table_iter_seek_to(struct table_iter *ti, uint64_t off, uint8_t typ)
328
ti->typ = block_reader_type(&ti->br);
329
ti->block_off = off;
330
block_iter_seek_start(&ti->bi, &ti->br);
331
+ ti->is_finished = 0;
332
return 0;
333
}
334
t/unit-tests/t-reftable-merged.c
+76
@@ -194,6 +194,81 @@ static void t_merged_refs(void)
194
reftable_free(bs);
195
}
196
197
+static void t_merged_seek_multiple_times(void)
198
+{
199
+ struct reftable_ref_record r1[] = {
200
+ {
201
+ .refname = (char *) "a",
202
+ .update_index = 1,
203
+ .value_type = REFTABLE_REF_VAL1,
204
+ .value.val1 = { 1 },
205
+ },
206
+ {
207
+ .refname = (char *) "c",
208
+ .update_index = 1,
209
+ .value_type = REFTABLE_REF_VAL1,
210
+ .value.val1 = { 2 },
211
+ }
212
+ };
213
+ struct reftable_ref_record r2[] = {
214
+ {
215
+ .refname = (char *) "b",
216
+ .update_index = 2,
217
+ .value_type = REFTABLE_REF_VAL1,
218
+ .value.val1 = { 3 },
219
+ },
220
+ {
221
+ .refname = (char *) "d",
222
+ .update_index = 2,
223
+ .value_type = REFTABLE_REF_VAL1,
224
+ .value.val1 = { 4 },
225
+ },
226
+ };
227
+ struct reftable_ref_record *refs[] = {
228
+ r1, r2,
229
+ };
230
+ size_t sizes[] = {
231
+ ARRAY_SIZE(r1), ARRAY_SIZE(r2),
232
+ };
233
+ struct strbuf bufs[] = {
234
+ STRBUF_INIT, STRBUF_INIT,
235
+ };
236
+ struct reftable_block_source *sources = NULL;
237
+ struct reftable_reader **readers = NULL;
238
+ struct reftable_ref_record rec = { 0 };
239
+ struct reftable_iterator it = { 0 };
240
+ struct reftable_merged_table *mt;
241
+
242
+ mt = merged_table_from_records(refs, &sources, &readers, sizes, bufs, 2);
243
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
244
+
245
+ for (size_t i = 0; i < 5; i++) {
246
+ int err = reftable_iterator_seek_ref(&it, "c");
247
+ check(!err);
248
+
249
+ err = reftable_iterator_next_ref(&it, &rec);
250
+ check(!err);
251
+ err = reftable_ref_record_equal(&rec, &r1[1], GIT_SHA1_RAWSZ);
252
+ check(err == 1);
253
+
254
+ err = reftable_iterator_next_ref(&it, &rec);
255
+ check(!err);
256
+ err = reftable_ref_record_equal(&rec, &r2[1], GIT_SHA1_RAWSZ);
257
+ check(err == 1);
258
+
259
+ err = reftable_iterator_next_ref(&it, &rec);
260
+ check(err > 0);
261
+ }
262
+
263
+ for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
264
+ strbuf_release(&bufs[i]);
265
+ readers_destroy(readers, ARRAY_SIZE(refs));
266
+ reftable_ref_record_release(&rec);
267
+ reftable_iterator_destroy(&it);
268
+ reftable_merged_table_free(mt);
269
+ reftable_free(sources);
270
+}
271
+
272
static struct reftable_merged_table *
273
merged_table_from_log_records(struct reftable_log_record **logs,
274
struct reftable_block_source **source,
@@ -383,6 +458,7 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
458
TEST(t_default_write_opts(), "merged table with default write opts");
459
TEST(t_merged_logs(), "merged table with multiple log updates for same ref");
460
TEST(t_merged_refs(), "merged table with multiple updates to same ref");
461
+ TEST(t_merged_seek_multiple_times(), "merged table can seek multiple times");
462
TEST(t_merged_single_record(), "ref ocurring in only one record can be fetched");
463
464
return test_done();
t/unit-tests/t-reftable-reader.c
new
+96
@@ -0,0 +1,96 @@
1
+#include "test-lib.h"
2
+#include "lib-reftable.h"
3
+#include "reftable/blocksource.h"
4
+#include "reftable/reader.h"
5
+
6
+static int t_reader_seek_once(void)
7
+{
8
+ struct reftable_ref_record records[] = {
9
+ {
10
+ .refname = (char *) "refs/heads/main",
11
+ .value_type = REFTABLE_REF_VAL1,
12
+ .value.val1 = { 42 },
13
+ },
14
+ };
15
+ struct reftable_block_source source = { 0 };
16
+ struct reftable_ref_record ref = { 0 };
17
+ struct reftable_iterator it = { 0 };
18
+ struct reftable_reader *reader;
19
+ struct strbuf buf = STRBUF_INIT;
20
+ int ret;
21
+
22
+ t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
23
+ block_source_from_strbuf(&source, &buf);
24
+
25
+ ret = reftable_reader_new(&reader, &source, "name");
26
+ check(!ret);
27
+
28
+ reftable_reader_init_ref_iterator(reader, &it);
29
+ ret = reftable_iterator_seek_ref(&it, "");
30
+ check(!ret);
31
+ ret = reftable_iterator_next_ref(&it, &ref);
32
+ check(!ret);
33
+
34
+ ret = reftable_ref_record_equal(&ref, &records[0], GIT_SHA1_RAWSZ);
35
+ check_int(ret, ==, 1);
36
+
37
+ ret = reftable_iterator_next_ref(&it, &ref);
38
+ check_int(ret, ==, 1);
39
+
40
+ reftable_ref_record_release(&ref);
41
+ reftable_iterator_destroy(&it);
42
+ reftable_reader_decref(reader);
43
+ strbuf_release(&buf);
44
+ return 0;
45
+}
46
+
47
+static int t_reader_reseek(void)
48
+{
49
+ struct reftable_ref_record records[] = {
50
+ {
51
+ .refname = (char *) "refs/heads/main",
52
+ .value_type = REFTABLE_REF_VAL1,
53
+ .value.val1 = { 42 },
54
+ },
55
+ };
56
+ struct reftable_block_source source = { 0 };
57
+ struct reftable_ref_record ref = { 0 };
58
+ struct reftable_iterator it = { 0 };
59
+ struct reftable_reader *reader;
60
+ struct strbuf buf = STRBUF_INIT;
61
+ int ret;
62
+
63
+ t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
64
+ block_source_from_strbuf(&source, &buf);
65
+
66
+ ret = reftable_reader_new(&reader, &source, "name");
67
+ check(!ret);
68
+
69
+ reftable_reader_init_ref_iterator(reader, &it);
70
+
71
+ for (size_t i = 0; i < 5; i++) {
72
+ ret = reftable_iterator_seek_ref(&it, "");
73
+ check(!ret);
74
+ ret = reftable_iterator_next_ref(&it, &ref);
75
+ check(!ret);
76
+
77
+ ret = reftable_ref_record_equal(&ref, &records[0], GIT_SHA1_RAWSZ);
78
+ check_int(ret, ==, 1);
79
+
80
+ ret = reftable_iterator_next_ref(&it, &ref);
81
+ check_int(ret, ==, 1);
82
+ }
83
+
84
+ reftable_ref_record_release(&ref);
85
+ reftable_iterator_destroy(&it);
86
+ reftable_reader_decref(reader);
87
+ strbuf_release(&buf);
88
+ return 0;
89
+}
90
+
91
+int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
92
+{
93
+ TEST(t_reader_seek_once(), "reader can seek once");
94
+ TEST(t_reader_reseek(), "reader can reseek multiple times");
95
+ return test_done();
96
+}