pgrx_pg_sys/submodules/
htup.rs

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