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}