Line data Source code
1 : #ifndef HEADER_fd_src_funk_fd_funk_rec_h
2 : #define HEADER_fd_src_funk_fd_funk_rec_h
3 :
4 : /* This provides APIs for managing funk records. It is generally not
5 : meant to be included directly. Use fd_funk.h instead. */
6 :
7 : #include "fd_funk_txn.h" /* Includes fd_funk_base.h */
8 :
9 : /* FD_FUNK_REC_{ALIGN,FOOTPRINT} describe the alignment and footprint of
10 : a fd_funk_rec_t. ALIGN will be a power of 2, footprint will be a
11 : multiple of align. These are provided to facilitate compile time
12 : declarations. */
13 :
14 : #define FD_FUNK_REC_ALIGN (32UL)
15 :
16 : /* FD_FUNK_REC_FLAG_* are flags that can be bit-ored together to specify
17 : how records are to be interpreted. The 5 most significant bytes of a
18 : rec's flag are reserved to be used in conjunction with the ERASE flag.
19 :
20 : - ERASE indicates a record in an in-preparation transaction should be
21 : erased if and when the in-preparation transaction is published. If
22 : set on a published record, it serves as a tombstone.
23 : If set, there will be no value resources used by this record. */
24 :
25 1099293285 : #define FD_FUNK_REC_FLAG_ERASE (1UL<<0)
26 :
27 : /* FD_FUNK_REC_IDX_NULL gives the map record idx value used to represent
28 : NULL. This value also set a limit on how large rec_max can be. */
29 :
30 5812484022 : #define FD_FUNK_REC_IDX_NULL (ULONG_MAX)
31 :
32 : /* FD_FUNK_PART_NULL is the partition number of records that are not
33 : in a partition */
34 962634945 : #define FD_FUNK_PART_NULL (UINT_MAX)
35 :
36 : /* A fd_funk_rec_t describes a funk record. */
37 :
38 : struct __attribute__((aligned(FD_FUNK_REC_ALIGN))) fd_funk_rec {
39 :
40 : /* These fields are managed by the funk's rec_map */
41 :
42 : fd_funk_xid_key_pair_t pair; /* Transaction id and record key pair */
43 : ulong map_next; /* Internal use by map */
44 : ulong map_hash; /* Internal use by map */
45 :
46 : /* These fields are managed by funk. TODO: Consider using record
47 : index compression here (much more debatable than in txn itself). */
48 :
49 : ulong prev_idx; /* Record map index of previous record */
50 : ulong next_idx; /* Record map index of next record */
51 : uint txn_cidx; /* Compressed transaction map index (or compressed FD_FUNK_TXN_IDX if this is in the last published) */
52 : uint tag; /* Internal use only */
53 : ulong flags; /* Flags that indicate how to interpret a record */
54 :
55 : /* Note: use of uint here requires FD_FUNK_REC_VAL_MAX to be at most
56 : UINT_MAX. */
57 :
58 : uint val_sz; /* Num bytes in record value, in [0,val_max] */
59 : uint val_max; /* Max byte in record value, in [0,FD_FUNK_REC_VAL_MAX], 0 if erase flag set or val_gaddr is 0 */
60 : ulong val_gaddr; /* Wksp gaddr on record value if any, 0 if erase flag set or val_max is 0
61 : If non-zero, the region [val_gaddr,val_gaddr+val_max) will be a current fd_alloc allocation (such that it is
62 : has tag wksp_tag) and the owner of the region will be the record. IMPORTANT! HAS NO GUARANTEED ALIGNMENT! */
63 :
64 : ulong prev_part_idx; /* Record map index of previous record in partition chain */
65 : ulong next_part_idx; /* Record map index of next record in partition chain */
66 : uint part; /* Partition number, FD_FUNK_PART_NULL if none */
67 :
68 : /* Padding to FD_FUNK_REC_ALIGN here (TODO: consider using self index
69 : in the structures to accelerate indexing computations if padding
70 : permits as this structure is currently has 8 bytes of padding) */
71 : };
72 :
73 : typedef struct fd_funk_rec fd_funk_rec_t;
74 :
75 : FD_STATIC_ASSERT( sizeof(fd_funk_rec_t) == 5U*32U, record size is wrong );
76 :
77 : /* fd_funk_rec_map allows for indexing records by their (xid,key) pair.
78 : It is used to store all records of the last published transaction and
79 : the records being updated for a transaction that is in-preparation.
80 : Published records are stored under the pair (root,key). (This is
81 : done so that publishing a transaction doesn't require updating all
82 : transaction id of all the records that were not updated by the
83 : publish.) */
84 :
85 : #define MAP_NAME fd_funk_rec_map
86 : #define MAP_T fd_funk_rec_t
87 : #define MAP_KEY_T fd_funk_xid_key_pair_t
88 : #define MAP_KEY pair
89 2294901225 : #define MAP_KEY_EQ(k0,k1) fd_funk_xid_key_pair_eq((k0),(k1))
90 881126784 : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
91 7856379 : #define MAP_KEY_COPY(kd,ks) fd_funk_xid_key_pair_copy((kd),(ks))
92 74802882816 : #define MAP_NEXT map_next
93 : #define MAP_HASH map_hash
94 : #define MAP_MAGIC (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
95 : #define MAP_IMPL_STYLE 1
96 : #define MAP_MEMOIZE 1
97 : #include "../util/tmpl/fd_map_giant.c"
98 :
99 : FD_PROTOTYPES_BEGIN
100 :
101 : FD_FN_PURE ulong fd_funk_rec_map_list_idx( fd_funk_rec_t const * join, fd_funk_xid_key_pair_t const * key );
102 :
103 : void fd_funk_rec_map_set_key_cnt( fd_funk_rec_t * join, ulong key_cnt );
104 :
105 : /* fd_funk_rec_idx_is_null returns 1 if idx is FD_FUNK_REC_IDX_NULL and
106 : 0 otherwise. */
107 :
108 5578636080 : FD_FN_CONST static inline int fd_funk_rec_idx_is_null( ulong idx ) { return idx==FD_FUNK_REC_IDX_NULL; }
109 :
110 : /* Accessors */
111 :
112 : /* fd_funk_rec_cnt returns the number of records in the record map.
113 : Assumes map==fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) where funk
114 : is a current local join. See fd_funk.h for fd_funk_rec_max. */
115 :
116 3 : FD_FN_PURE static inline ulong fd_funk_rec_cnt( fd_funk_rec_t const * map ) { return fd_funk_rec_map_key_cnt( map ); }
117 :
118 : /* fd_funk_rec_is_full returns 1 if the record map is full (i.e. the
119 : maximum of records that can be concurrently tracked has been reached)
120 : and 0 otherwise. Note that this includes all the records in the last
121 : published transactions and records being updated by in-preparation
122 : transactions. Assumes funk is a current local join and
123 : map==fd_funk_rec_map( funk, fd_funk_wksp( funk ) ). */
124 :
125 33484209 : FD_FN_PURE static inline int fd_funk_rec_is_full( fd_funk_rec_t const * map ) { return fd_funk_rec_map_is_full( map ); }
126 :
127 : /* fd_funk_rec_query queries the in-preparation transaction pointed to
128 : by txn for the record whose key matches the key pointed to by key.
129 : If txn is NULL, the query will be done for the funk's last published
130 : transaction. Returns a pointer to current record on success and NULL
131 : on failure. Reasons for failure include txn is neither NULL nor a
132 : pointer to a in-preparation transaction, key is NULL or not a record
133 : in the given transaction.
134 :
135 : The returned pointer is in the caller's address space and, if the
136 : return value is non-NULL, the lifetime of the returned pointer is the
137 : lesser of the current local join, the key is removed from the
138 : transaction, the lifetime of the in-preparation transaction (txn is
139 : non-NULL) or the next successful publication (txn is NULL).
140 :
141 : Assumes funk is a current local join (NULL returns NULL), txn is NULL
142 : or points to an in-preparation transaction in the caller's address
143 : space, key points to a record key in the caller's address space (NULL
144 : returns NULL), and no concurrent operations on funk, txn or key.
145 : funk retains no interest in key. The funk retains ownership of any
146 : returned record. The record value metadata will be updated whenever
147 : the record value modified.
148 :
149 : This is reasonably fast O(1).
150 :
151 : Important safety tip! This function can encounter records
152 : that have the ERASE flag set (i.e. are tombstones of erased
153 : records). fd_funk_rec_query will still return the record in this
154 : case, and the application should check for the flag. */
155 :
156 : FD_FN_PURE fd_funk_rec_t const *
157 : fd_funk_rec_query( fd_funk_t * funk,
158 : fd_funk_txn_t const * txn,
159 : fd_funk_rec_key_t const * key );
160 :
161 :
162 : /* fd_funk_rec_query_global is the same as fd_funk_rec_query but will
163 : query txn's ancestors for key from youngest to oldest if key is not
164 : part of txn. As such, the txn of the returned record may not match
165 : txn but will be the txn of most recent ancestor with the key
166 : otherwise. *txn_out is set to the transaction where the record was
167 : found.
168 :
169 : This is reasonably fast O(in_prep_ancestor_cnt).
170 :
171 : Important safety tip! This function can encounter records
172 : that have the ERASE flag set (i.e. are tombstones of erased
173 : records). fd_funk_rec_query_global will return a NULL in this case
174 : but still set *txn_out to the relevant transaction. This behavior
175 : differs from fd_funk_rec_query. */
176 : FD_FN_PURE fd_funk_rec_t const *
177 : fd_funk_rec_query_global( fd_funk_t * funk,
178 : fd_funk_txn_t const * txn,
179 : fd_funk_rec_key_t const * key,
180 : fd_funk_txn_t const ** txn_out );
181 :
182 : /* fd_funk_rec_query_safe is a query that is safe in the presence of
183 : concurrent writes. The result data is copied into a buffer
184 : allocated by the given valloc and should be freed with the same
185 : valloc. NULL is returned if the query fails. The query is always
186 : against the root transaction. */
187 :
188 : FD_FN_PURE void *
189 : fd_funk_rec_query_safe( fd_funk_t * funk,
190 : fd_funk_rec_key_t const * key,
191 : fd_valloc_t valloc,
192 : ulong * result_len );
193 :
194 : FD_FN_PURE void *
195 : fd_funk_rec_query_xid_safe( fd_funk_t * funk,
196 : fd_funk_rec_key_t const * key,
197 : fd_funk_txn_xid_t const * xid,
198 : fd_valloc_t valloc,
199 : ulong * result_len );
200 :
201 : /* fd_funk_rec_test tests the record pointed to by rec. Returns
202 : FD_FUNK_SUCCESS (0) if rec appears to be a live unfrozen record in
203 : funk and a FD_FUNK_ERR_* (negative) otherwise. Specifically:
204 :
205 : FD_FUNK_ERR_INVAL - bad inputs (NULL funk, NULL rec, rec is clearly
206 : not from funk, etc)
207 :
208 : FD_FUNK_ERR_KEY - the record did not appear to be a live record.
209 : Specifically rec's (xid,key) did not resolve to itself.
210 :
211 : FD_FUNK_ERR_XID - memory corruption was detected in testing rec
212 :
213 : FD_FUNK_ERR_FROZEN - rec is part of a frozen transaction
214 :
215 : If fd_funk_rec_test returns SUCCESS, modify and remove are guaranteed
216 : to succeed immediately after return. The value returned by test will
217 : stable for the same lifetime as a modify.
218 :
219 : Assumes funk is a current local join (NULL returns NULL).
220 :
221 : This is a reasonably fast O(1). */
222 :
223 : FD_FN_PURE int
224 : fd_funk_rec_test( fd_funk_t * funk,
225 : fd_funk_rec_t const * rec );
226 :
227 : /* fd_funk_rec_{pair,xid,key} returns a pointer in the local address
228 : space of the {(transaction id,record key) pair,transaction id,record
229 : key} of a live record. Assumes rec points to a live record in the
230 : caller's address space. The lifetime of the returned pointer is the
231 : same as rec. The value at the pointer will be constant for its
232 : lifetime. */
233 :
234 383345772 : FD_FN_CONST static inline fd_funk_xid_key_pair_t const * fd_funk_rec_pair( fd_funk_rec_t const * rec ) { return &rec->pair; }
235 2316412857 : FD_FN_CONST static inline fd_funk_txn_xid_t const * fd_funk_rec_xid ( fd_funk_rec_t const * rec ) { return rec->pair.xid; }
236 1182153372 : FD_FN_CONST static inline fd_funk_rec_key_t const * fd_funk_rec_key ( fd_funk_rec_t const * rec ) { return rec->pair.key; }
237 :
238 : /* fd_funk_rec_txn returns the in-preparation transaction to which the
239 : live record rec belongs or NULL if rec belongs to the last published
240 : transaction.
241 :
242 : fd_funk_rec_{next,prev} returns the {next,prev} record
243 : ({younger,older} record) of the set of records to which rec belongs
244 : or NULL if rec is the {youngest,oldest}.
245 :
246 : fd_funk_txn_rec_{head,tail} returns the {head,tail} record
247 : ({oldest,youngest} record) of the in-preparation transaction to which
248 : rec belongs or NULL if rec_next is the {youngest,oldest} in that
249 : transaction.
250 :
251 : All pointers are in the caller's address space. These are all a fast
252 : O(1) but not fortified against memory data corruption. */
253 :
254 : FD_FN_PURE static inline fd_funk_txn_t const * /* Lifetime as described in fd_funk_txn_query */
255 : fd_funk_rec_txn( fd_funk_rec_t const * rec, /* Assumes live funk record, funk current local join */
256 295009464 : fd_funk_txn_t const * txn_map ) { /* Assumes == fd_funk_txn_map( funk, fd_funk_wksp( funk ) ) */
257 295009464 : ulong txn_idx = fd_funk_txn_idx( rec->txn_cidx );
258 295009464 : if( fd_funk_txn_idx_is_null( txn_idx ) ) return NULL; /* TODO: consider branchless */
259 85444671 : return txn_map + txn_idx;
260 295009464 : }
261 :
262 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
263 : fd_funk_rec_next( fd_funk_rec_t const * rec, /* Assumes live funk record, funk current local join */
264 567620916 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
265 567620916 : ulong rec_idx = rec->next_idx;
266 567620916 : if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL; /* TODO: consider branchless */
267 522504174 : return rec_map + rec_idx;
268 567620916 : }
269 :
270 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
271 : fd_funk_rec_prev( fd_funk_rec_t const * rec, /* Assumes live funk record, funk current local join */
272 283810458 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
273 283810458 : ulong rec_idx = rec->prev_idx;
274 283810458 : if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL; /* TODO: consider branchless */
275 261252087 : return rec_map + rec_idx;
276 283810458 : }
277 :
278 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
279 : fd_funk_txn_rec_head( fd_funk_txn_t const * txn, /* Assumes an in-preparation transaction, funk current local join */
280 110022453 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
281 110022453 : ulong rec_head_idx = txn->rec_head_idx;
282 110022453 : if( fd_funk_rec_idx_is_null( rec_head_idx ) ) return NULL; /* TODO: consider branchless */
283 102035463 : return rec_map + rec_head_idx;
284 110022453 : }
285 :
286 : FD_FN_PURE static inline fd_funk_rec_t const * /* Lifetime as described in fd_funk_rec_query */
287 : fd_funk_txn_rec_tail( fd_funk_txn_t const * txn, /* Assumes an in-preparation transaction, funk current local join */
288 82622811 : fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
289 82622811 : ulong rec_tail_idx = txn->rec_tail_idx;
290 82622811 : if( fd_funk_rec_idx_is_null( rec_tail_idx ) ) return NULL; /* TODO: consider branchless */
291 82622811 : return rec_map + rec_tail_idx;
292 82622811 : }
293 :
294 : /* fd_funk_rec_modify returns rec as a non-const rec if rec is currently
295 : safe to modify the val / discard a change to the record for an
296 : in-preparation transaction (incl discard an erase) / erase a
297 : published record / etc. Reasons for NULL include NULL funk, NULL
298 : rec, rec does not appear to be a live record, or the transaction to
299 : which rec belongs is frozen.
300 :
301 : The returned pointer is in the caller's address space and, if the
302 : return value is non-NULL, the lifetime of the returned pointer is the
303 : the lesser of the current local join, the key has been removed from
304 : the transaction, the transaction becomes frozen due to birth of an
305 : in-preparation child, (for a key part of an in-preparation
306 : transaction) the lifetime of in-preparation transaction, (for a key
307 : part of the last published transaction) the next successful
308 : publication.
309 :
310 : Assumes funk is a current local join (NULL returns NULL), rec is a
311 : pointer in the caller's address space to a fd_funk_rec_t (NULL
312 : returns NULL), and no concurrent operations on funk or rec. The funk
313 : retains ownership of rec. The record value metadata will be updated
314 : whenever the record value modified.
315 :
316 : This is a reasonably fast O(1). */
317 :
318 : FD_FN_PURE fd_funk_rec_t *
319 : fd_funk_rec_modify( fd_funk_t * funk,
320 : fd_funk_rec_t const * rec );
321 :
322 : /* Returns 1 if the record has been modified in its transaction
323 : compared to the prior incarnation of the record with the same
324 : key (or there is no prior incarnation). Returns -1 if rec is part
325 : of a published transaction. Return 0 otherwise. */
326 :
327 : FD_FN_PURE int
328 : fd_funk_rec_is_modified( fd_funk_t * funk,
329 : fd_funk_rec_t const * rec );
330 :
331 : /* TODO: Consider instead doing something like: modify_init, modify_fini and
332 : preventing forking the txn if records are being modified instead of
333 : the long laundry list of lifetime constraints? */
334 :
335 : /* fd_funk_rec_insert inserts a new record whose key will be the same
336 : as the record key pointed to by key to the in-preparation transaction
337 : pointed to by txn. If txn is NULL, the record will be inserted into
338 : the last published transaction.
339 :
340 : Returns a pointer to the new record on success and NULL on failure.
341 : If opt_err is non-NULL, on return, *opt_err will indicate the result
342 : of the operation.
343 :
344 : FD_FUNK_SUCCESS - success
345 :
346 : FD_FUNK_ERR_INVAL - failed due to bad inputs (NULL funk, NULL key,
347 : txn was neither NULL nor pointer to an in-preparation
348 : transaction)
349 :
350 : FD_FUNK_ERR_REC - failed due to too many records in the func,
351 : increase rec_max
352 :
353 : FD_FUNK_ERR_FROZEN - txn is a transaction that is a parent of an
354 : in-preparation transaction.
355 :
356 : FD_FUNK_ERR_KEY - key referred to an record that is already present
357 : in the transaction.
358 :
359 : The returned pointer is in the caller's address space and, if the
360 : return value is non-NULL, the lifetime of the returned pointer is the
361 : lesser of the current local join, the record is removed, the txn's
362 : lifetime (only applicable if txn is non-NULL) or the next successful
363 : publication (only applicable if txn is NULL).
364 :
365 : Note, if this insert is for a record that in txn with the ERASE flag
366 : set, the ERASE flag of the record will be cleared and it will return
367 : that record.
368 :
369 : Assumes funk is a current local join (NULL returns NULL), txn is NULL
370 : or points to an in-preparation transaction in the caller's address
371 : space, key points to a record key in the caller's address space (NULL
372 : returns NULL), and no concurrent operations on funk, txn or key.
373 : funk retains no interest in key or opt_err. The funk retains
374 : ownership of txn and any returned record. The record value metadata
375 : will be updated whenever the record value modified.
376 :
377 : This is a reasonably fast O(1) and fortified against memory
378 : corruption.
379 :
380 : Note that when a record is newly created, it is initially created
381 : with a NULL value. If intending to modify the value in the most
382 : recent ancestor version of the record, a record can be loaded with
383 : the data via:
384 :
385 : fd_funk_val_copy( rec, fd_funk_val_const( orig_rec ), fd_funk_val_sz( orig_rec ), 0UL, alloc, wksp, NULL );
386 :
387 : // Note: could use fd_funk_val_max( orig_rec ) or some other
388 : // intelligence about planned changes via sz_est instead of 0UL
389 :
390 : This is O(orig_rec) size. If the caller doesn't have the
391 : original record lying around, it can be found via:
392 :
393 : fd_funk_rec_t const * orig_rec = fd_funk_rec_query_global( funk, txn_parent, key, NULL );
394 :
395 : This is O(ancestor depth to orig rec) and accounts for that the
396 : previous version of the record might not be in txn's parent. */
397 :
398 : fd_funk_rec_t const *
399 : fd_funk_rec_insert( fd_funk_t * funk,
400 : fd_funk_txn_t * txn,
401 : fd_funk_rec_key_t const * key,
402 : int * opt_err );
403 :
404 : /* fd_funk_rec_remove removes the live record pointed to by rec from
405 : the funk. Returns FD_FUNK_SUCCESS (0) on success and a FD_FUNK_ERR_*
406 : (negative) on failure. Reasons for failure include:
407 :
408 : FD_FUNK_ERR_INVAL - bad inputs (NULL funk, NULL rec, rec is
409 : obviously not from funk, etc)
410 :
411 : FD_FUNK_ERR_KEY - the record did not appear to be a live record.
412 : Specifically, a record query of funk for rec's (xid,key) pair did
413 : not return rec.
414 :
415 : The record will cease to exist in that transaction and any of
416 : transaction's subsequently created descendants (again, assuming no
417 : subsequent insert of key). This type of remove can be done on a
418 : published record (assuming the last published transaction is
419 : unfrozen). A tombstone is left in funk to track removals as they
420 : are published or cancelled.
421 :
422 : Any information in an erased record is lost.
423 :
424 : Assumes funk is a current local join (NULL returns ERR_INVAL) and rec
425 : points to a record in the caller's address space (NULL returns
426 : ERR_INVAL). As the funk still has ownership of rec before and after
427 : the call if live, the user doesn't need to, for example, match
428 : inserts with removes.
429 :
430 : This is a reasonably fast O(1) and fortified against memory corruption.
431 :
432 : IMPORTANT SAFETY TIP! DO NOT CAST AWAY CONST FROM A FD_FUNK_REC_T TO
433 : USE THIS FUNCTION (E.G. PASS A RESULT DIRECTLY FROM QUERY). USE A
434 : LIVE RESULT FROM FD_FUNK_REC_MODIFY! */
435 :
436 : int
437 : fd_funk_rec_remove( fd_funk_t * funk,
438 : fd_funk_rec_t * rec,
439 : ulong erase_data );
440 :
441 :
442 : /* When a record is erased there is metadata stored in the five most
443 : significant bytes of a record. These are helpers to make setting
444 : and getting these values simple. The caller is responsible for doing
445 : a check on the flag of the record before using the value of the erase
446 : data. The 5 least significant bytes of the erase data parameter will
447 : be used and set into the erase flag. */
448 :
449 : void
450 : fd_funk_rec_set_erase_data( fd_funk_rec_t * rec, ulong erase_data );
451 :
452 : ulong
453 : fd_funk_rec_get_erase_data( fd_funk_rec_t const * rec );
454 :
455 : /* Remove a list of tombstones from funk, thereby freeing up space in
456 : the main index. All the records must be removed and published
457 : beforehand. Reasons for failure include:
458 :
459 : FD_FUNK_ERR_INVAL - bad inputs (NULL funk, NULL rec, rec is
460 : obviously not from funk, etc)
461 :
462 : FD_FUNK_ERR_KEY - the record did not appear to be a removed record.
463 : Specifically, a record query of funk for rec's (xid,key) pair did
464 : not return rec. Also, the record was never published.
465 : */
466 : int
467 : fd_funk_rec_forget( fd_funk_t * funk,
468 : fd_funk_rec_t ** recs,
469 : ulong recs_cnt );
470 :
471 : /* fd_funk_rec_write_prepare combines several operations into one
472 : convenient package. There are 3 basic cases:
473 :
474 : 1. If the given record key already exists in the transaction, the
475 : record is returned in modifiable form. This is equivalent to
476 : fd_funk_rec_query combined with fd_funk_rec_modify.
477 :
478 : 2. If the record key is entirely new (not present in the
479 : transaction or any of its ancestors), a new record is inserted and
480 : returned in modifiable form. This is equivalent to
481 : fd_funk_rec_insert combined with fd_funk_rec_modify. Note that if
482 : the do_create argument is false, a NULL will be returned in this case.
483 :
484 : 3. Otherwise, the record is copied from the ancestor transaction
485 : into the given transaction. This is returned in modifiable
486 : form. This is equivalent to fd_funk_rec_query_global,
487 : fd_funk_rec_insert, fd_funk_val_copy, and fd_funk_rec_modify.
488 :
489 : In all cases, the record is grown to min_val_size if it is less
490 : than this size, padding with zeros if necessary.
491 :
492 : The irec argument is the previous incarnation of the record if
493 : known (i.e. the result of fd_funk_rec_query_global( funk, txn, key
494 : ) ). This allows the elimination of the query in some cases. Use
495 : NULL if this value is unavailable. */
496 : fd_funk_rec_t *
497 : fd_funk_rec_write_prepare( fd_funk_t * funk, /* Funky database */
498 : fd_funk_txn_t * txn, /* Write the record into this transaction */
499 : fd_funk_rec_key_t const * key, /* Key of new/modified record */
500 : ulong min_val_size, /* Minimum value size of writable record */
501 : int do_create, /* Can create new record */
502 : fd_funk_rec_t const * irec, /* Prior result of fd_funk_rec_query_global if known */
503 : int * opt_err ); /* Optional error code return */
504 :
505 : /* Misc */
506 :
507 : /* fd_funk_rec_verify verifies the record map. Returns FD_FUNK_SUCCESS
508 : if the record map appears intact and FD_FUNK_ERR_INVAL if not (logs
509 : details). Meant to be called as part of fd_funk_verify. As such, it
510 : assumes funk is non-NULL, fd_funk_{wksp,txn_map,rec_map} have been
511 : verified to work and the txn_map has been verified. */
512 :
513 : int
514 : fd_funk_rec_verify( fd_funk_t * funk );
515 :
516 : FD_PROTOTYPES_END
517 :
518 : #endif /* HEADER_fd_src_funk_fd_funk_rec_h */
|