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,
12    FormData_pg_attribute, FrozenTransactionId, HeapTupleData, HeapTupleHeaderData, TransactionId,
13    TupleDesc, HEAP_HASNULL, HEAP_HOT_UPDATED, HEAP_NATTS_MASK, HEAP_ONLY_TUPLE, HEAP_XMAX_INVALID,
14    HEAP_XMIN_COMMITTED, HEAP_XMIN_FROZEN, 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)]
202unsafe fn fetchatt(A: *const FormData_pg_attribute, T: *mut std::os::raw::c_char) -> Datum {
203    // #define fetchatt(A,T) fetch_att(T, (A)->attbyval, (A)->attlen)
204
205    unsafe {
206        // SAFETY:  caller has asserted `A` is a valid FromData_pg_attribute pointer
207        fetch_att(T, (*A).attbyval, (*A).attlen)
208    }
209}
210
211/// Given a Form_pg_attribute and a pointer into a tuple's data area,
212/// return the correct value or pointer.
213///
214/// We return a Datum value in all cases.  If the attribute has "byval" false,
215/// we return the same pointer into the tuple data area that we're passed.
216/// Otherwise, we return the correct number of bytes fetched from the data
217/// area and extended to Datum form.
218///
219/// On machines where Datum is 8 bytes, we support fetching 8-byte byval
220/// attributes; otherwise, only 1, 2, and 4-byte values are supported.
221///
222/// # Safety
223///
224/// Note that T must be non-null and already properly aligned for this to work correctly.
225#[inline(always)]
226unsafe fn fetch_att(T: *mut std::os::raw::c_char, attbyval: bool, attlen: i16) -> Datum {
227    unsafe {
228        // #define fetch_att(T,attbyval,attlen) \
229        // ( \
230        // 	(attbyval) ? \
231        // 	( \
232        // 		(attlen) == (int) sizeof(Datum) ? \
233        // 			*((Datum *)(T)) \
234        // 		: \
235        // 	  ( \
236        // 		(attlen) == (int) sizeof(int32) ? \
237        // 			Int32GetDatum(*((int32 *)(T))) \
238        // 		: \
239        // 		( \
240        // 			(attlen) == (int) sizeof(int16) ? \
241        // 				Int16GetDatum(*((int16 *)(T))) \
242        // 			: \
243        // 			( \
244        // 				AssertMacro((attlen) == 1), \
245        // 				CharGetDatum(*((char *)(T))) \
246        // 			) \
247        // 		) \
248        // 	  ) \
249        // 	) \
250        // 	: \
251        // 	PointerGetDatum((char *) (T)) \
252        // )
253
254        // SAFETY:  The only "unsafe" below is dereferencing T, and the caller has assured us it's non-null
255        if attbyval {
256            let attlen = attlen as usize;
257
258            // NB:  Compiler should solve this branch for us, and we write it like this to avoid
259            // code duplication for the case where a Datum isn't 8 bytes wide
260            if SIZEOF_DATUM == 8 && attlen == std::mem::size_of::<Datum>() {
261                return *T.cast::<Datum>();
262            }
263
264            if attlen == std::mem::size_of::<i32>() {
265                Datum::from(*T.cast::<i32>())
266            } else if attlen == std::mem::size_of::<i16>() {
267                Datum::from(*T.cast::<i16>())
268            } else {
269                assert_eq!(attlen, 1);
270                Datum::from(*T.cast::<std::os::raw::c_char>())
271            }
272        } else {
273            Datum::from(T.cast::<std::os::raw::c_char>())
274        }
275    }
276}
277
278/// Extract an attribute of a heap tuple and return it as a Datum.
279/// This works for either system or user attributes.  The given attnum
280/// is properly range-checked.
281///
282/// If the field in question has a NULL value, we return a zero [`Datum`]
283/// and set `*isnull == true`.  Otherwise, we set `*isnull == false`.
284///
285/// # Safety
286///
287/// - `tup` is the pointer to the heap tuple.
288/// - `attnum` is the **1-based** attribute number of the column (field) caller wants.
289/// - `tupleDesc` is a pointer to the structure describing the row and all its fields.
290///
291/// These things must complement each other correctly
292#[inline(always)]
293pub unsafe fn heap_getattr(
294    tup: *mut HeapTupleData,
295    attnum: i32,
296    tupleDesc: TupleDesc,
297    isnull: &mut bool,
298) -> Datum {
299    // static inline Datum
300    // heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
301    // {
302    // 	if (attnum > 0)
303    // 	{
304    // 		if (attnum > (int) HeapTupleHeaderGetNatts(tup->t_data))
305    // 			return getmissingattr(tupleDesc, attnum, isnull);
306    // 		else
307    // 			return fastgetattr(tup, attnum, tupleDesc, isnull);
308    // 	}
309    // 	else
310    // 		return heap_getsysattr(tup, attnum, tupleDesc, isnull);
311    // }
312
313    unsafe {
314        // SAFETY:  caller has asserted that `tup` and `tupleDesc` are valid pointers
315        if attnum > 0 {
316            if attnum > HeapTupleHeaderGetNatts((*tup).t_data) as i32 {
317                getmissingattr(tupleDesc, attnum, isnull)
318            } else {
319                fastgetattr(tup, attnum, tupleDesc, isnull)
320            }
321        } else {
322            heap_getsysattr(tup, attnum, tupleDesc, isnull)
323        }
324    }
325}
326
327/// Fetch a user attribute's value as a Datum (might be either a
328/// value, or a pointer into the data area of the tuple).
329///
330/// # Safety
331///
332/// This must not be used when a system attribute might be requested.
333/// Furthermore, the passed attnum MUST be valid.  Use [heap_getattr]
334/// instead, if in doubt.
335///
336/// # Panics
337///
338/// Will panic if `attnum` is less than one
339#[inline(always)]
340unsafe fn fastgetattr(
341    tup: *mut HeapTupleData,
342    attnum: i32,
343    tupleDesc: TupleDesc,
344    isnull: &mut bool,
345) -> Datum {
346    // static inline Datum
347    // fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
348    // {
349    // 	Assert(attnum > 0);
350    //
351    // 	*isnull = false;
352    // 	if (HeapTupleNoNulls(tup))
353    // 	{
354    // 		Form_pg_attribute att;
355    //
356    // 		att = TupleDescAttr(tupleDesc, attnum - 1);
357    // 		if (att->attcacheoff >= 0)
358    // 			return fetchatt(att, (char *) tup->t_data + tup->t_data->t_hoff +
359    // 							att->attcacheoff);
360    // 		else
361    // 			return nocachegetattr(tup, attnum, tupleDesc);
362    // 	}
363    // 	else
364    // 	{
365    // 		if (att_isnull(attnum - 1, tup->t_data->t_bits))
366    // 		{
367    // 			*isnull = true;
368    // 			return (Datum) NULL;
369    // 		}
370    // 		else
371    // 			return nocachegetattr(tup, attnum, tupleDesc);
372    // 	}
373    // }
374
375    assert!(attnum > 0);
376
377    unsafe {
378        *isnull = false;
379        if HeapTupleNoNulls(tup) {
380            let att = &(*tupleDesc).attrs.as_slice((*tupleDesc).natts as _)[attnum as usize - 1];
381            if att.attcacheoff >= 0 {
382                let t_data = (*tup).t_data;
383                fetchatt(
384                    att,
385                    t_data
386                        .cast::<std::os::raw::c_char>()
387                        .add((*t_data).t_hoff as usize + att.attcacheoff as usize),
388                )
389            } else {
390                nocachegetattr(tup, attnum, tupleDesc)
391            }
392        } else if att_isnull(attnum - 1, (*(*tup).t_data).t_bits.as_ptr()) {
393            *isnull = true;
394            Datum::from(0) // a NULL pointer
395        } else {
396            nocachegetattr(tup, attnum, tupleDesc)
397        }
398    }
399}