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}