1 /*-------------------------------------------------------------------------
2 *
3 * datum.c
4 * POSTGRES Datum (abstract data type) manipulation routines.
5 *
6 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/utils/adt/datum.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16 /*
17 * In the implementation of these routines we assume the following:
18 *
19 * A) if a type is "byVal" then all the information is stored in the
20 * Datum itself (i.e. no pointers involved!). In this case the
21 * length of the type is always greater than zero and not more than
22 * "sizeof(Datum)"
23 *
24 * B) if a type is not "byVal" and it has a fixed length (typlen > 0),
25 * then the "Datum" always contains a pointer to a stream of bytes.
26 * The number of significant bytes are always equal to the typlen.
27 *
28 * C) if a type is not "byVal" and has typlen == -1,
29 * then the "Datum" always points to a "struct varlena".
30 * This varlena structure has information about the actual length of this
31 * particular instance of the type and about its value.
32 *
33 * D) if a type is not "byVal" and has typlen == -2,
34 * then the "Datum" always points to a null-terminated C string.
35 *
36 * Note that we do not treat "toasted" datums specially; therefore what
37 * will be copied or compared is the compressed data or toast reference.
38 * An exception is made for datumCopy() of an expanded object, however,
39 * because most callers expect to get a simple contiguous (and pfree'able)
40 * result from datumCopy(). See also datumTransfer().
41 */
42
43 #include "postgres.h"
44
45 #include "utils/datum.h"
46 #include "utils/expandeddatum.h"
47
48
49 /*-------------------------------------------------------------------------
50 * datumGetSize
51 *
52 * Find the "real" size of a datum, given the datum value,
53 * whether it is a "by value", and the declared type length.
54 * (For TOAST pointer datums, this is the size of the pointer datum.)
55 *
56 * This is essentially an out-of-line version of the att_addlength_datum()
57 * macro in access/tupmacs.h. We do a tad more error checking though.
58 *-------------------------------------------------------------------------
59 */
60 Size
datumGetSize(Datum value,bool typByVal,int typLen)61 datumGetSize(Datum value, bool typByVal, int typLen)
62 {
63 Size size;
64
65 if (typByVal)
66 {
67 /* Pass-by-value types are always fixed-length */
68 Assert(typLen > 0 && typLen <= sizeof(Datum));
69 size = (Size) typLen;
70 }
71 else
72 {
73 if (typLen > 0)
74 {
75 /* Fixed-length pass-by-ref type */
76 size = (Size) typLen;
77 }
78 else if (typLen == -1)
79 {
80 /* It is a varlena datatype */
81 struct varlena *s = (struct varlena *) DatumGetPointer(value);
82
83 if (!PointerIsValid(s))
84 ereport(ERROR,
85 (errcode(ERRCODE_DATA_EXCEPTION),
86 errmsg("invalid Datum pointer")));
87
88 size = (Size) VARSIZE_ANY(s);
89 }
90 else if (typLen == -2)
91 {
92 /* It is a cstring datatype */
93 char *s = (char *) DatumGetPointer(value);
94
95 if (!PointerIsValid(s))
96 ereport(ERROR,
97 (errcode(ERRCODE_DATA_EXCEPTION),
98 errmsg("invalid Datum pointer")));
99
100 size = (Size) (strlen(s) + 1);
101 }
102 else
103 {
104 elog(ERROR, "invalid typLen: %d", typLen);
105 size = 0; /* keep compiler quiet */
106 }
107 }
108
109 return size;
110 }
111
112 /*-------------------------------------------------------------------------
113 * datumCopy
114 *
115 * Make a copy of a non-NULL datum.
116 *
117 * If the datatype is pass-by-reference, memory is obtained with palloc().
118 *
119 * If the value is a reference to an expanded object, we flatten into memory
120 * obtained with palloc(). We need to copy because one of the main uses of
121 * this function is to copy a datum out of a transient memory context that's
122 * about to be destroyed, and the expanded object is probably in a child
123 * context that will also go away. Moreover, many callers assume that the
124 * result is a single pfree-able chunk.
125 *-------------------------------------------------------------------------
126 */
127 Datum
datumCopy(Datum value,bool typByVal,int typLen)128 datumCopy(Datum value, bool typByVal, int typLen)
129 {
130 Datum res;
131
132 if (typByVal)
133 res = value;
134 else if (typLen == -1)
135 {
136 /* It is a varlena datatype */
137 struct varlena *vl = (struct varlena *) DatumGetPointer(value);
138
139 if (VARATT_IS_EXTERNAL_EXPANDED(vl))
140 {
141 /* Flatten into the caller's memory context */
142 ExpandedObjectHeader *eoh = DatumGetEOHP(value);
143 Size resultsize;
144 char *resultptr;
145
146 resultsize = EOH_get_flat_size(eoh);
147 resultptr = (char *) palloc(resultsize);
148 EOH_flatten_into(eoh, (void *) resultptr, resultsize);
149 res = PointerGetDatum(resultptr);
150 }
151 else
152 {
153 /* Otherwise, just copy the varlena datum verbatim */
154 Size realSize;
155 char *resultptr;
156
157 realSize = (Size) VARSIZE_ANY(vl);
158 resultptr = (char *) palloc(realSize);
159 memcpy(resultptr, vl, realSize);
160 res = PointerGetDatum(resultptr);
161 }
162 }
163 else
164 {
165 /* Pass by reference, but not varlena, so not toasted */
166 Size realSize;
167 char *resultptr;
168
169 realSize = datumGetSize(value, typByVal, typLen);
170
171 resultptr = (char *) palloc(realSize);
172 memcpy(resultptr, DatumGetPointer(value), realSize);
173 res = PointerGetDatum(resultptr);
174 }
175 return res;
176 }
177
178 /*-------------------------------------------------------------------------
179 * datumTransfer
180 *
181 * Transfer a non-NULL datum into the current memory context.
182 *
183 * This is equivalent to datumCopy() except when the datum is a read-write
184 * pointer to an expanded object. In that case we merely reparent the object
185 * into the current context, and return its standard R/W pointer (in case the
186 * given one is a transient pointer of shorter lifespan).
187 *-------------------------------------------------------------------------
188 */
189 Datum
datumTransfer(Datum value,bool typByVal,int typLen)190 datumTransfer(Datum value, bool typByVal, int typLen)
191 {
192 if (!typByVal && typLen == -1 &&
193 VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(value)))
194 value = TransferExpandedObject(value, CurrentMemoryContext);
195 else
196 value = datumCopy(value, typByVal, typLen);
197 return value;
198 }
199
200 /*-------------------------------------------------------------------------
201 * datumIsEqual
202 *
203 * Return true if two datums are equal, false otherwise
204 *
205 * NOTE: XXX!
206 * We just compare the bytes of the two values, one by one.
207 * This routine will return false if there are 2 different
208 * representations of the same value (something along the lines
209 * of say the representation of zero in one's complement arithmetic).
210 * Also, it will probably not give the answer you want if either
211 * datum has been "toasted".
212 *
213 * Do not try to make this any smarter than it currently is with respect
214 * to "toasted" datums, because some of the callers could be working in the
215 * context of an aborted transaction.
216 *-------------------------------------------------------------------------
217 */
218 bool
datumIsEqual(Datum value1,Datum value2,bool typByVal,int typLen)219 datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
220 {
221 bool res;
222
223 if (typByVal)
224 {
225 /*
226 * just compare the two datums. NOTE: just comparing "len" bytes will
227 * not do the work, because we do not know how these bytes are aligned
228 * inside the "Datum". We assume instead that any given datatype is
229 * consistent about how it fills extraneous bits in the Datum.
230 */
231 res = (value1 == value2);
232 }
233 else
234 {
235 Size size1,
236 size2;
237 char *s1,
238 *s2;
239
240 /*
241 * Compare the bytes pointed by the pointers stored in the datums.
242 */
243 size1 = datumGetSize(value1, typByVal, typLen);
244 size2 = datumGetSize(value2, typByVal, typLen);
245 if (size1 != size2)
246 return false;
247 s1 = (char *) DatumGetPointer(value1);
248 s2 = (char *) DatumGetPointer(value2);
249 res = (memcmp(s1, s2, size1) == 0);
250 }
251 return res;
252 }
253
254 /*-------------------------------------------------------------------------
255 * datumEstimateSpace
256 *
257 * Compute the amount of space that datumSerialize will require for a
258 * particular Datum.
259 *-------------------------------------------------------------------------
260 */
261 Size
datumEstimateSpace(Datum value,bool isnull,bool typByVal,int typLen)262 datumEstimateSpace(Datum value, bool isnull, bool typByVal, int typLen)
263 {
264 Size sz = sizeof(int);
265
266 if (!isnull)
267 {
268 /* no need to use add_size, can't overflow */
269 if (typByVal)
270 sz += sizeof(Datum);
271 else if (typLen == -1 &&
272 VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
273 {
274 /* Expanded objects need to be flattened, see comment below */
275 sz += EOH_get_flat_size(DatumGetEOHP(value));
276 }
277 else
278 sz += datumGetSize(value, typByVal, typLen);
279 }
280
281 return sz;
282 }
283
284 /*-------------------------------------------------------------------------
285 * datumSerialize
286 *
287 * Serialize a possibly-NULL datum into caller-provided storage.
288 *
289 * Note: "expanded" objects are flattened so as to produce a self-contained
290 * representation, but other sorts of toast pointers are transferred as-is.
291 * This is because the intended use of this function is to pass the value
292 * to another process within the same database server. The other process
293 * could not access an "expanded" object within this process's memory, but
294 * we assume it can dereference the same TOAST pointers this one can.
295 *
296 * The format is as follows: first, we write a 4-byte header word, which
297 * is either the length of a pass-by-reference datum, -1 for a
298 * pass-by-value datum, or -2 for a NULL. If the value is NULL, nothing
299 * further is written. If it is pass-by-value, sizeof(Datum) bytes
300 * follow. Otherwise, the number of bytes indicated by the header word
301 * follow. The caller is responsible for ensuring that there is enough
302 * storage to store the number of bytes that will be written; use
303 * datumEstimateSpace() to find out how many will be needed.
304 * *start_address is updated to point to the byte immediately following
305 * those written.
306 *-------------------------------------------------------------------------
307 */
308 void
datumSerialize(Datum value,bool isnull,bool typByVal,int typLen,char ** start_address)309 datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
310 char **start_address)
311 {
312 ExpandedObjectHeader *eoh = NULL;
313 int header;
314
315 /* Write header word. */
316 if (isnull)
317 header = -2;
318 else if (typByVal)
319 header = -1;
320 else if (typLen == -1 &&
321 VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
322 {
323 eoh = DatumGetEOHP(value);
324 header = EOH_get_flat_size(eoh);
325 }
326 else
327 header = datumGetSize(value, typByVal, typLen);
328 memcpy(*start_address, &header, sizeof(int));
329 *start_address += sizeof(int);
330
331 /* If not null, write payload bytes. */
332 if (!isnull)
333 {
334 if (typByVal)
335 {
336 memcpy(*start_address, &value, sizeof(Datum));
337 *start_address += sizeof(Datum);
338 }
339 else if (eoh)
340 {
341 char *tmp;
342
343 /*
344 * EOH_flatten_into expects the target address to be maxaligned,
345 * so we can't store directly to *start_address.
346 */
347 tmp = (char *) palloc(header);
348 EOH_flatten_into(eoh, (void *) tmp, header);
349 memcpy(*start_address, tmp, header);
350 *start_address += header;
351
352 /* be tidy. */
353 pfree(tmp);
354 }
355 else
356 {
357 memcpy(*start_address, DatumGetPointer(value), header);
358 *start_address += header;
359 }
360 }
361 }
362
363 /*-------------------------------------------------------------------------
364 * datumRestore
365 *
366 * Restore a possibly-NULL datum previously serialized by datumSerialize.
367 * *start_address is updated according to the number of bytes consumed.
368 *-------------------------------------------------------------------------
369 */
370 Datum
datumRestore(char ** start_address,bool * isnull)371 datumRestore(char **start_address, bool *isnull)
372 {
373 int header;
374 void *d;
375
376 /* Read header word. */
377 memcpy(&header, *start_address, sizeof(int));
378 *start_address += sizeof(int);
379
380 /* If this datum is NULL, we can stop here. */
381 if (header == -2)
382 {
383 *isnull = true;
384 return (Datum) 0;
385 }
386
387 /* OK, datum is not null. */
388 *isnull = false;
389
390 /* If this datum is pass-by-value, sizeof(Datum) bytes follow. */
391 if (header == -1)
392 {
393 Datum val;
394
395 memcpy(&val, *start_address, sizeof(Datum));
396 *start_address += sizeof(Datum);
397 return val;
398 }
399
400 /* Pass-by-reference case; copy indicated number of bytes. */
401 Assert(header > 0);
402 d = palloc(header);
403 memcpy(d, *start_address, header);
404 *start_address += header;
405 return PointerGetDatum(d);
406 }
407