1 /*-------------------------------------------------------------------------
2  *
3  * tupmacs.h
4  *	  Tuple macros used by both index tuples and heap tuples.
5  *
6  *
7  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * src/include/access/tupmacs.h
11  *
12  *-------------------------------------------------------------------------
13  */
14 #ifndef TUPMACS_H
15 #define TUPMACS_H
16 
17 #include "catalog/pg_type_d.h"	/* for TYPALIGN macros */
18 
19 
20 /*
21  * Check a tuple's null bitmap to determine whether the attribute is null.
22  * Note that a 0 in the null bitmap indicates a null, while 1 indicates
23  * non-null.
24  */
25 #define att_isnull(ATT, BITS) (!((BITS)[(ATT) >> 3] & (1 << ((ATT) & 0x07))))
26 
27 /*
28  * Given a Form_pg_attribute and a pointer into a tuple's data area,
29  * return the correct value or pointer.
30  *
31  * We return a Datum value in all cases.  If the attribute has "byval" false,
32  * we return the same pointer into the tuple data area that we're passed.
33  * Otherwise, we return the correct number of bytes fetched from the data
34  * area and extended to Datum form.
35  *
36  * On machines where Datum is 8 bytes, we support fetching 8-byte byval
37  * attributes; otherwise, only 1, 2, and 4-byte values are supported.
38  *
39  * Note that T must already be properly aligned for this to work correctly.
40  */
41 #define fetchatt(A,T) fetch_att(T, (A)->attbyval, (A)->attlen)
42 
43 /*
44  * Same, but work from byval/len parameters rather than Form_pg_attribute.
45  */
46 #if SIZEOF_DATUM == 8
47 
48 #define fetch_att(T,attbyval,attlen) \
49 ( \
50 	(attbyval) ? \
51 	( \
52 		(attlen) == (int) sizeof(Datum) ? \
53 			*((Datum *)(T)) \
54 		: \
55 	  ( \
56 		(attlen) == (int) sizeof(int32) ? \
57 			Int32GetDatum(*((int32 *)(T))) \
58 		: \
59 		( \
60 			(attlen) == (int) sizeof(int16) ? \
61 				Int16GetDatum(*((int16 *)(T))) \
62 			: \
63 			( \
64 				AssertMacro((attlen) == 1), \
65 				CharGetDatum(*((char *)(T))) \
66 			) \
67 		) \
68 	  ) \
69 	) \
70 	: \
71 	PointerGetDatum((char *) (T)) \
72 )
73 #else							/* SIZEOF_DATUM != 8 */
74 
75 #define fetch_att(T,attbyval,attlen) \
76 ( \
77 	(attbyval) ? \
78 	( \
79 		(attlen) == (int) sizeof(int32) ? \
80 			Int32GetDatum(*((int32 *)(T))) \
81 		: \
82 		( \
83 			(attlen) == (int) sizeof(int16) ? \
84 				Int16GetDatum(*((int16 *)(T))) \
85 			: \
86 			( \
87 				AssertMacro((attlen) == 1), \
88 				CharGetDatum(*((char *)(T))) \
89 			) \
90 		) \
91 	) \
92 	: \
93 	PointerGetDatum((char *) (T)) \
94 )
95 #endif							/* SIZEOF_DATUM == 8 */
96 
97 /*
98  * att_align_datum aligns the given offset as needed for a datum of alignment
99  * requirement attalign and typlen attlen.  attdatum is the Datum variable
100  * we intend to pack into a tuple (it's only accessed if we are dealing with
101  * a varlena type).  Note that this assumes the Datum will be stored as-is;
102  * callers that are intending to convert non-short varlena datums to short
103  * format have to account for that themselves.
104  */
105 #define att_align_datum(cur_offset, attalign, attlen, attdatum) \
106 ( \
107 	((attlen) == -1 && VARATT_IS_SHORT(DatumGetPointer(attdatum))) ? \
108 	(uintptr_t) (cur_offset) : \
109 	att_align_nominal(cur_offset, attalign) \
110 )
111 
112 /*
113  * att_align_pointer performs the same calculation as att_align_datum,
114  * but is used when walking a tuple.  attptr is the current actual data
115  * pointer; when accessing a varlena field we have to "peek" to see if we
116  * are looking at a pad byte or the first byte of a 1-byte-header datum.
117  * (A zero byte must be either a pad byte, or the first byte of a correctly
118  * aligned 4-byte length word; in either case we can align safely.  A non-zero
119  * byte must be either a 1-byte length word, or the first byte of a correctly
120  * aligned 4-byte length word; in either case we need not align.)
121  *
122  * Note: some callers pass a "char *" pointer for cur_offset.  This is
123  * a bit of a hack but should work all right as long as uintptr_t is the
124  * correct width.
125  */
126 #define att_align_pointer(cur_offset, attalign, attlen, attptr) \
127 ( \
128 	((attlen) == -1 && VARATT_NOT_PAD_BYTE(attptr)) ? \
129 	(uintptr_t) (cur_offset) : \
130 	att_align_nominal(cur_offset, attalign) \
131 )
132 
133 /*
134  * att_align_nominal aligns the given offset as needed for a datum of alignment
135  * requirement attalign, ignoring any consideration of packed varlena datums.
136  * There are three main use cases for using this macro directly:
137  *	* we know that the att in question is not varlena (attlen != -1);
138  *	  in this case it is cheaper than the above macros and just as good.
139  *	* we need to estimate alignment padding cost abstractly, ie without
140  *	  reference to a real tuple.  We must assume the worst case that
141  *	  all varlenas are aligned.
142  *	* within arrays and multiranges, we unconditionally align varlenas (XXX this
143  *	  should be revisited, probably).
144  *
145  * The attalign cases are tested in what is hopefully something like their
146  * frequency of occurrence.
147  */
148 #define att_align_nominal(cur_offset, attalign) \
149 ( \
150 	((attalign) == TYPALIGN_INT) ? INTALIGN(cur_offset) : \
151 	 (((attalign) == TYPALIGN_CHAR) ? (uintptr_t) (cur_offset) : \
152 	  (((attalign) == TYPALIGN_DOUBLE) ? DOUBLEALIGN(cur_offset) : \
153 	   ( \
154 			AssertMacro((attalign) == TYPALIGN_SHORT), \
155 			SHORTALIGN(cur_offset) \
156 	   ))) \
157 )
158 
159 /*
160  * att_addlength_datum increments the given offset by the space needed for
161  * the given Datum variable.  attdatum is only accessed if we are dealing
162  * with a variable-length attribute.
163  */
164 #define att_addlength_datum(cur_offset, attlen, attdatum) \
165 	att_addlength_pointer(cur_offset, attlen, DatumGetPointer(attdatum))
166 
167 /*
168  * att_addlength_pointer performs the same calculation as att_addlength_datum,
169  * but is used when walking a tuple --- attptr is the pointer to the field
170  * within the tuple.
171  *
172  * Note: some callers pass a "char *" pointer for cur_offset.  This is
173  * actually perfectly OK, but probably should be cleaned up along with
174  * the same practice for att_align_pointer.
175  */
176 #define att_addlength_pointer(cur_offset, attlen, attptr) \
177 ( \
178 	((attlen) > 0) ? \
179 	( \
180 		(cur_offset) + (attlen) \
181 	) \
182 	: (((attlen) == -1) ? \
183 	( \
184 		(cur_offset) + VARSIZE_ANY(attptr) \
185 	) \
186 	: \
187 	( \
188 		AssertMacro((attlen) == -2), \
189 		(cur_offset) + (strlen((char *) (attptr)) + 1) \
190 	)) \
191 )
192 
193 /*
194  * store_att_byval is a partial inverse of fetch_att: store a given Datum
195  * value into a tuple data area at the specified address.  However, it only
196  * handles the byval case, because in typical usage the caller needs to
197  * distinguish by-val and by-ref cases anyway, and so a do-it-all macro
198  * wouldn't be convenient.
199  */
200 #if SIZEOF_DATUM == 8
201 
202 #define store_att_byval(T,newdatum,attlen) \
203 	do { \
204 		switch (attlen) \
205 		{ \
206 			case sizeof(char): \
207 				*(char *) (T) = DatumGetChar(newdatum); \
208 				break; \
209 			case sizeof(int16): \
210 				*(int16 *) (T) = DatumGetInt16(newdatum); \
211 				break; \
212 			case sizeof(int32): \
213 				*(int32 *) (T) = DatumGetInt32(newdatum); \
214 				break; \
215 			case sizeof(Datum): \
216 				*(Datum *) (T) = (newdatum); \
217 				break; \
218 			default: \
219 				elog(ERROR, "unsupported byval length: %d", \
220 					 (int) (attlen)); \
221 				break; \
222 		} \
223 	} while (0)
224 #else							/* SIZEOF_DATUM != 8 */
225 
226 #define store_att_byval(T,newdatum,attlen) \
227 	do { \
228 		switch (attlen) \
229 		{ \
230 			case sizeof(char): \
231 				*(char *) (T) = DatumGetChar(newdatum); \
232 				break; \
233 			case sizeof(int16): \
234 				*(int16 *) (T) = DatumGetInt16(newdatum); \
235 				break; \
236 			case sizeof(int32): \
237 				*(int32 *) (T) = DatumGetInt32(newdatum); \
238 				break; \
239 			default: \
240 				elog(ERROR, "unsupported byval length: %d", \
241 					 (int) (attlen)); \
242 				break; \
243 		} \
244 	} while (0)
245 #endif							/* SIZEOF_DATUM == 8 */
246 
247 #endif
248