pgx_pg_sys/submodules/htup.rs
1use crate::{
2 bits8, getmissingattr, heap_getsysattr, nocachegetattr, CommandId, Datum,
3 FormData_pg_attribute, FrozenTransactionId, HeapTupleData, HeapTupleHeaderData, TransactionId,
4 TupleDesc, HEAP_HASNULL, HEAP_HOT_UPDATED, HEAP_NATTS_MASK, HEAP_ONLY_TUPLE, HEAP_XMAX_INVALID,
5 HEAP_XMIN_COMMITTED, HEAP_XMIN_FROZEN, HEAP_XMIN_INVALID, SIZEOF_DATUM,
6};
7
8/// # Safety
9///
10/// Caller must ensure `tup` is a valid [`HeapTupleHeaderData`] pointer
11#[inline(always)]
12pub unsafe fn HeapTupleHeaderIsHeapOnly(tup: *const HeapTupleHeaderData) -> bool {
13 // #define HeapTupleHeaderIsHeapOnly(tup) \
14 // ( \
15 // ((tup)->t_infomask2 & HEAP_ONLY_TUPLE) != 0 \
16 // )
17
18 unsafe {
19 // SAFETY: caller has asserted `htup_header` is a valid HeapTupleHeaderData pointer
20 ((*tup).t_infomask2 & HEAP_ONLY_TUPLE as u16) != 0
21 }
22}
23
24/// # Safety
25///
26/// Caller must ensure `tup` is a valid [`HeapTupleHeaderData`] pointer
27#[inline(always)]
28pub unsafe fn HeapTupleHeaderIsHotUpdated(tup: *const HeapTupleHeaderData) -> bool {
29 // #define HeapTupleHeaderIsHotUpdated(tup) \
30 // ( \
31 // ((tup)->t_infomask2 & HEAP_HOT_UPDATED) != 0 && \
32 // ((tup)->t_infomask & HEAP_XMAX_INVALID) == 0 && \
33 // !HeapTupleHeaderXminInvalid(tup) \
34 // )
35
36 unsafe {
37 // SAFETY: caller has asserted `htup_header` is a valid HeapTupleHeaderData pointer
38 (*tup).t_infomask2 & HEAP_HOT_UPDATED as u16 != 0
39 && (*tup).t_infomask & HEAP_XMAX_INVALID as u16 == 0
40 && !HeapTupleHeaderXminInvalid(tup)
41 }
42}
43
44/// # Safety
45///
46/// Caller must ensure `tup` is a valid [`HeapTupleHeaderData`] pointer
47#[inline(always)]
48pub unsafe fn HeapTupleHeaderXminInvalid(tup: *const HeapTupleHeaderData) -> bool {
49 // #define HeapTupleHeaderXminInvalid(tup) \
50 // ( \
51 // ((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)) == \
52 // HEAP_XMIN_INVALID \
53 // )
54
55 unsafe {
56 // SAFETY: caller has asserted `htup_header` is a valid HeapTupleHeaderData pointer
57 (*tup).t_infomask & (HEAP_XMIN_COMMITTED as u16 | HEAP_XMIN_INVALID as u16)
58 == HEAP_XMIN_INVALID as u16
59 }
60}
61
62/// Does the specified [`HeapTupleHeaderData`] represent a "frozen" tuple?
63///
64/// # Safety
65///
66/// Caller must ensure `tup` is a valid [`HeapTupleHeaderData`] pointer
67#[inline(always)]
68pub unsafe fn HeapTupleHeaderFrozen(tup: *const HeapTupleHeaderData) -> bool {
69 // #define HeapTupleHeaderXminFrozen(tup) \
70 // ( \
71 // ((tup)->t_infomask & (HEAP_XMIN_FROZEN)) == HEAP_XMIN_FROZEN \
72 // )
73
74 unsafe {
75 // SAFETY: caller has asserted `tup` is a valid HeapTupleHeader pointer
76 (*tup).t_infomask & (HEAP_XMIN_FROZEN as u16) == (HEAP_XMIN_FROZEN as u16)
77 }
78}
79
80/// HeapTupleHeaderGetRawCommandId will give you what's in the header whether
81/// it is useful or not. Most code should use HeapTupleHeaderGetCmin or
82/// HeapTupleHeaderGetCmax instead, but note that those Assert that you can
83/// get a legitimate result, ie you are in the originating transaction!
84///
85/// # Safety
86///
87/// Caller must ensure `tup` is a valid [`HeapTupleHeaderData`] pointer
88#[inline(always)]
89pub unsafe fn HeapTupleGetRawCommandId(tup: *const HeapTupleHeaderData) -> CommandId {
90 // #define HeapTupleHeaderGetRawCommandId(tup) \
91 // ( \
92 // (tup)->t_choice.t_heap.t_field3.t_cid \
93 // )
94
95 unsafe {
96 // SAFETY: caller has asserted `tup` is a valid HeapTupleHeader pointer
97 (*tup).t_choice.t_heap.t_field3.t_cid
98 }
99}
100
101/// HeapTupleHeaderGetRawXmin returns the "raw" xmin field, which is the xid
102/// originally used to insert the tuple. However, the tuple might actually
103/// be frozen (via HeapTupleHeaderSetXminFrozen) in which case the tuple's xmin
104/// is visible to every snapshot. Prior to PostgreSQL 9.4, we actually changed
105/// the xmin to FrozenTransactionId, and that value may still be encountered
106/// on disk.
107///
108/// # Safety
109///
110/// Caller must ensure `tup` is a valid [`HeapTupleHeaderData`] pointer
111#[inline(always)]
112pub unsafe fn HeapTupleHeaderGetRawXmin(tup: *const HeapTupleHeaderData) -> TransactionId {
113 // #define HeapTupleHeaderGetRawXmin(tup) \
114 // ( \
115 // (tup)->t_choice.t_heap.t_xmin \
116 // )
117 unsafe {
118 // SAFETY: caller has asserted `tup` is a valid HeapTupleHeader pointer
119 (*tup).t_choice.t_heap.t_xmin
120 }
121}
122
123/// Returns the `xmin` value of the specified [`HeapTupleHeaderData`]
124///
125/// # Safety
126///
127/// Caller must ensure `tup` is a valid [`HeapTupleHeaderData`] pointer
128#[inline(always)]
129pub unsafe fn HeapTupleHeaderGetXmin(tup: *const HeapTupleHeaderData) -> TransactionId {
130 // #define HeapTupleHeaderGetXmin(tup) \
131 // ( \
132 // HeapTupleHeaderXminFrozen(tup) ? \
133 // FrozenTransactionId : HeapTupleHeaderGetRawXmin(tup) \
134 // )
135
136 unsafe {
137 // SAFETY: caller has asserted `tup` is a valid HeapTupleHeader pointer
138 if HeapTupleHeaderFrozen(tup) {
139 FrozenTransactionId
140 } else {
141 HeapTupleHeaderGetRawXmin(tup)
142 }
143 }
144}
145
146/// How many attributes does the specified [`HeapTupleHeader`] have?
147///
148/// # Safety
149///
150/// Caller is responsible for ensuring `tup` is a valid pointer
151#[inline(always)]
152pub unsafe fn HeapTupleHeaderGetNatts(tup: *const HeapTupleHeaderData) -> u16 {
153 // #define HeapTupleHeaderGetNatts(tup) \
154 // ((tup)->t_infomask2 & HEAP_NATTS_MASK)
155 unsafe {
156 // SAFETY: caller has asserted that `tup` is a valid, non-null, pointer to a HeapTupleHeaderData struct
157 (*tup).t_infomask2 & (HEAP_NATTS_MASK as u16)
158 }
159}
160
161/// Does the specified [`HeapTuple`] (`tup`) contain nulls?
162///
163/// # Safety
164///
165/// Caller is responsible for ensuring `tup` is a valid pointer
166#[inline(always)]
167pub unsafe fn HeapTupleNoNulls(tup: *const HeapTupleData) -> bool {
168 // #define HeapTupleNoNulls(tuple) \
169 // (!((tuple)->t_data->t_infomask & HEAP_HASNULL))
170
171 unsafe {
172 // SAFETY: caller has asserted that 'tup' is a valid, non-null pointer to a HeapTuple struct
173 (*(*tup).t_data).t_infomask & (HEAP_HASNULL as u16) == 0
174 }
175}
176
177/// # Safety
178///
179/// Caller is responsible for ensuring `BITS` is a valid [`bits8`] pointer of the right length to
180/// accommodate `ATT >> 3`
181#[inline(always)]
182unsafe fn att_isnull(ATT: i32, BITS: *const bits8) -> bool {
183 // #define att_isnull(ATT, BITS) (!((BITS)[(ATT) >> 3] & (1 << ((ATT) & 0x07))))
184 let ATT = ATT as usize;
185 let slot = BITS.add(ATT >> 3);
186 (*slot & (1 << (ATT & 0x07))) == 0
187}
188
189/// # Safety
190///
191/// Caller is responsible for ensuring `A` is a valid [`FormData_pg_attribute`] pointer
192#[inline(always)]
193unsafe fn fetchatt(A: *const FormData_pg_attribute, T: *mut std::os::raw::c_char) -> Datum {
194 // #define fetchatt(A,T) fetch_att(T, (A)->attbyval, (A)->attlen)
195
196 unsafe {
197 // SAFETY: caller has asserted `A` is a valid FromData_pg_attribute pointer
198 fetch_att(T, (*A).attbyval, (*A).attlen)
199 }
200}
201
202/// Given a Form_pg_attribute and a pointer into a tuple's data area,
203/// return the correct value or pointer.
204///
205/// We return a Datum value in all cases. If the attribute has "byval" false,
206/// we return the same pointer into the tuple data area that we're passed.
207/// Otherwise, we return the correct number of bytes fetched from the data
208/// area and extended to Datum form.
209///
210/// On machines where Datum is 8 bytes, we support fetching 8-byte byval
211/// attributes; otherwise, only 1, 2, and 4-byte values are supported.
212///
213/// # Safety
214///
215/// Note that T must be non-null and already properly aligned for this to work correctly.
216#[inline(always)]
217unsafe fn fetch_att(T: *mut std::os::raw::c_char, attbyval: bool, attlen: i16) -> Datum {
218 unsafe {
219 // #define fetch_att(T,attbyval,attlen) \
220 // ( \
221 // (attbyval) ? \
222 // ( \
223 // (attlen) == (int) sizeof(Datum) ? \
224 // *((Datum *)(T)) \
225 // : \
226 // ( \
227 // (attlen) == (int) sizeof(int32) ? \
228 // Int32GetDatum(*((int32 *)(T))) \
229 // : \
230 // ( \
231 // (attlen) == (int) sizeof(int16) ? \
232 // Int16GetDatum(*((int16 *)(T))) \
233 // : \
234 // ( \
235 // AssertMacro((attlen) == 1), \
236 // CharGetDatum(*((char *)(T))) \
237 // ) \
238 // ) \
239 // ) \
240 // ) \
241 // : \
242 // PointerGetDatum((char *) (T)) \
243 // )
244
245 // SAFETY: The only "unsafe" below is dereferencing T, and the caller has assured us it's non-null
246 if attbyval {
247 let attlen = attlen as usize;
248
249 // NB: Compiler should solve this branch for us, and we write it like this to avoid
250 // code duplication for the case where a Datum isn't 8 bytes wide
251 if SIZEOF_DATUM == 8 {
252 if attlen == std::mem::size_of::<Datum>() {
253 return *T.cast::<Datum>();
254 }
255 }
256
257 if attlen == std::mem::size_of::<i32>() {
258 Datum::from(*T.cast::<i32>())
259 } else {
260 if attlen == std::mem::size_of::<i16>() {
261 Datum::from(*T.cast::<i16>())
262 } else {
263 assert_eq!(attlen, 1);
264 Datum::from(*T.cast::<std::os::raw::c_char>())
265 }
266 }
267 } else {
268 Datum::from(T.cast::<std::os::raw::c_char>())
269 }
270 }
271}
272
273/// Extract an attribute of a heap tuple and return it as a Datum.
274/// This works for either system or user attributes. The given attnum
275/// is properly range-checked.
276///
277/// If the field in question has a NULL value, we return a zero [`Datum`]
278/// and set `*isnull == true`. Otherwise, we set `*isnull == false`.
279///
280/// # Safety
281///
282/// - `tup` is the pointer to the heap tuple.
283/// - `attnum` is the **1-based** attribute number of the column (field) caller wants.
284/// - `tupleDesc` is a pointer to the structure describing the row and all its fields.
285///
286/// These things must complement each other correctly
287#[inline(always)]
288pub unsafe fn heap_getattr(
289 tup: *mut HeapTupleData,
290 attnum: i32,
291 tupleDesc: TupleDesc,
292 isnull: &mut bool,
293) -> Datum {
294 // static inline Datum
295 // heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
296 // {
297 // if (attnum > 0)
298 // {
299 // if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
300 // return getmissingattr(tupleDesc, attnum, isnull);
301 // else
302 // return fastgetattr(tup, attnum, tupleDesc, isnull);
303 // }
304 // else
305 // return heap_getsysattr(tup, attnum, tupleDesc, isnull);
306 // }
307
308 unsafe {
309 // SAFETY: caller has asserted that `tup` and `tupleDesc` are valid pointers
310 if attnum > 0 {
311 if attnum > HeapTupleHeaderGetNatts((*tup).t_data) as i32 {
312 getmissingattr(tupleDesc, attnum, isnull)
313 } else {
314 fastgetattr(tup, attnum, tupleDesc, isnull)
315 }
316 } else {
317 heap_getsysattr(tup, attnum, tupleDesc, isnull)
318 }
319 }
320}
321
322/// Fetch a user attribute's value as a Datum (might be either a
323/// value, or a pointer into the data area of the tuple).
324///
325/// # Safety
326///
327/// This must not be used when a system attribute might be requested.
328/// Furthermore, the passed attnum MUST be valid. Use [heap_getattr]
329/// instead, if in doubt.
330///
331/// # Panics
332///
333/// Will panic if `attnum` is less than zero
334#[inline(always)]
335unsafe fn fastgetattr(
336 tup: *mut HeapTupleData,
337 attnum: i32,
338 tupleDesc: TupleDesc,
339 isnull: &mut bool,
340) -> Datum {
341 // static inline Datum
342 // fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
343 // {
344 // Assert(attnum > 0);
345 //
346 // *isnull = false;
347 // if (HeapTupleNoNulls(tup))
348 // {
349 // Form_pg_attribute att;
350 //
351 // att = TupleDescAttr(tupleDesc, attnum - 1);
352 // if (att->attcacheoff >= 0)
353 // return fetchatt(att, (char *) tup->t_data + tup->t_data->t_hoff +
354 // att->attcacheoff);
355 // else
356 // return nocachegetattr(tup, attnum, tupleDesc);
357 // }
358 // else
359 // {
360 // if (att_isnull(attnum - 1, tup->t_data->t_bits))
361 // {
362 // *isnull = true;
363 // return (Datum) NULL;
364 // }
365 // else
366 // return nocachegetattr(tup, attnum, tupleDesc);
367 // }
368 // }
369
370 assert!(attnum > 0);
371
372 unsafe {
373 *isnull = false;
374 if HeapTupleNoNulls(tup) {
375 let att = &(*tupleDesc).attrs.as_slice((*tupleDesc).natts as _)[attnum as usize - 1];
376 if att.attcacheoff >= 0 {
377 let t_data = (*tup).t_data;
378 fetchatt(
379 att,
380 t_data
381 .cast::<std::os::raw::c_char>()
382 .add((*t_data).t_hoff as usize + att.attcacheoff as usize),
383 )
384 } else {
385 nocachegetattr(tup, attnum, tupleDesc)
386 }
387 } else {
388 if att_isnull(attnum - 1, (*(*tup).t_data).t_bits.as_ptr()) {
389 *isnull = true;
390 Datum::from(0) // a NULL pointer
391 } else {
392 nocachegetattr(tup, attnum, tupleDesc)
393 }
394 }
395 }
396}