PostgreSQL Source Code  git master
tupmacs.h
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * tupmacs.h
4  * Tuple macros used by both index tuples and heap tuples.
5  *
6  *
7  * Portions Copyright (c) 1996-2020, 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, we unconditionally align varlenas (XXX this should be
143  * 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