Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * datum.c
4 : * POSTGRES Datum (abstract data type) manipulation routines.
5 : *
6 : * Portions Copyright (c) 1996-2021, 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 "access/detoast.h"
46 : #include "fmgr.h"
47 : #include "utils/builtins.h"
48 : #include "utils/datum.h"
49 : #include "utils/expandeddatum.h"
50 :
51 :
52 : /*-------------------------------------------------------------------------
53 : * datumGetSize
54 : *
55 : * Find the "real" size of a datum, given the datum value,
56 : * whether it is a "by value", and the declared type length.
57 : * (For TOAST pointer datums, this is the size of the pointer datum.)
58 : *
59 : * This is essentially an out-of-line version of the att_addlength_datum()
60 : * macro in access/tupmacs.h. We do a tad more error checking though.
61 : *-------------------------------------------------------------------------
62 : */
63 : Size
64 8232796 : datumGetSize(Datum value, bool typByVal, int typLen)
65 : {
66 : Size size;
67 :
68 8232796 : if (typByVal)
69 : {
70 : /* Pass-by-value types are always fixed-length */
71 : Assert(typLen > 0 && typLen <= sizeof(Datum));
72 1154388 : size = (Size) typLen;
73 : }
74 : else
75 : {
76 7078408 : if (typLen > 0)
77 : {
78 : /* Fixed-length pass-by-ref type */
79 5153878 : size = (Size) typLen;
80 : }
81 1924530 : else if (typLen == -1)
82 : {
83 : /* It is a varlena datatype */
84 1827730 : struct varlena *s = (struct varlena *) DatumGetPointer(value);
85 :
86 1827730 : if (!PointerIsValid(s))
87 0 : ereport(ERROR,
88 : (errcode(ERRCODE_DATA_EXCEPTION),
89 : errmsg("invalid Datum pointer")));
90 :
91 1827730 : size = (Size) VARSIZE_ANY(s);
92 : }
93 96800 : else if (typLen == -2)
94 : {
95 : /* It is a cstring datatype */
96 96800 : char *s = (char *) DatumGetPointer(value);
97 :
98 96800 : if (!PointerIsValid(s))
99 0 : ereport(ERROR,
100 : (errcode(ERRCODE_DATA_EXCEPTION),
101 : errmsg("invalid Datum pointer")));
102 :
103 96800 : size = (Size) (strlen(s) + 1);
104 : }
105 : else
106 : {
107 0 : elog(ERROR, "invalid typLen: %d", typLen);
108 : size = 0; /* keep compiler quiet */
109 : }
110 : }
111 :
112 8232796 : return size;
113 : }
114 :
115 : /*-------------------------------------------------------------------------
116 : * datumCopy
117 : *
118 : * Make a copy of a non-NULL datum.
119 : *
120 : * If the datatype is pass-by-reference, memory is obtained with palloc().
121 : *
122 : * If the value is a reference to an expanded object, we flatten into memory
123 : * obtained with palloc(). We need to copy because one of the main uses of
124 : * this function is to copy a datum out of a transient memory context that's
125 : * about to be destroyed, and the expanded object is probably in a child
126 : * context that will also go away. Moreover, many callers assume that the
127 : * result is a single pfree-able chunk.
128 : *-------------------------------------------------------------------------
129 : */
130 : Datum
131 18882164 : datumCopy(Datum value, bool typByVal, int typLen)
132 : {
133 : Datum res;
134 :
135 18882164 : if (typByVal)
136 10408412 : res = value;
137 8473752 : else if (typLen == -1)
138 : {
139 : /* It is a varlena datatype */
140 4653480 : struct varlena *vl = (struct varlena *) DatumGetPointer(value);
141 :
142 4653480 : if (VARATT_IS_EXTERNAL_EXPANDED(vl))
143 208 : {
144 : /* Flatten into the caller's memory context */
145 208 : ExpandedObjectHeader *eoh = DatumGetEOHP(value);
146 : Size resultsize;
147 : char *resultptr;
148 :
149 208 : resultsize = EOH_get_flat_size(eoh);
150 208 : resultptr = (char *) palloc(resultsize);
151 208 : EOH_flatten_into(eoh, (void *) resultptr, resultsize);
152 208 : res = PointerGetDatum(resultptr);
153 : }
154 : else
155 : {
156 : /* Otherwise, just copy the varlena datum verbatim */
157 : Size realSize;
158 : char *resultptr;
159 :
160 4653272 : realSize = (Size) VARSIZE_ANY(vl);
161 4653272 : resultptr = (char *) palloc(realSize);
162 4653272 : memcpy(resultptr, vl, realSize);
163 4653272 : res = PointerGetDatum(resultptr);
164 : }
165 : }
166 : else
167 : {
168 : /* Pass by reference, but not varlena, so not toasted */
169 : Size realSize;
170 : char *resultptr;
171 :
172 3820272 : realSize = datumGetSize(value, typByVal, typLen);
173 :
174 3820272 : resultptr = (char *) palloc(realSize);
175 3820272 : memcpy(resultptr, DatumGetPointer(value), realSize);
176 3820272 : res = PointerGetDatum(resultptr);
177 : }
178 18882164 : return res;
179 : }
180 :
181 : /*-------------------------------------------------------------------------
182 : * datumTransfer
183 : *
184 : * Transfer a non-NULL datum into the current memory context.
185 : *
186 : * This is equivalent to datumCopy() except when the datum is a read-write
187 : * pointer to an expanded object. In that case we merely reparent the object
188 : * into the current context, and return its standard R/W pointer (in case the
189 : * given one is a transient pointer of shorter lifespan).
190 : *-------------------------------------------------------------------------
191 : */
192 : Datum
193 32594 : datumTransfer(Datum value, bool typByVal, int typLen)
194 : {
195 32594 : if (!typByVal && typLen == -1 &&
196 31406 : VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(value)))
197 3576 : value = TransferExpandedObject(value, CurrentMemoryContext);
198 : else
199 29018 : value = datumCopy(value, typByVal, typLen);
200 32594 : return value;
201 : }
202 :
203 : /*-------------------------------------------------------------------------
204 : * datumIsEqual
205 : *
206 : * Return true if two datums are equal, false otherwise
207 : *
208 : * NOTE: XXX!
209 : * We just compare the bytes of the two values, one by one.
210 : * This routine will return false if there are 2 different
211 : * representations of the same value (something along the lines
212 : * of say the representation of zero in one's complement arithmetic).
213 : * Also, it will probably not give the answer you want if either
214 : * datum has been "toasted".
215 : *
216 : * Do not try to make this any smarter than it currently is with respect
217 : * to "toasted" datums, because some of the callers could be working in the
218 : * context of an aborted transaction.
219 : *-------------------------------------------------------------------------
220 : */
221 : bool
222 2694480 : datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
223 : {
224 : bool res;
225 :
226 2694480 : if (typByVal)
227 : {
228 : /*
229 : * just compare the two datums. NOTE: just comparing "len" bytes will
230 : * not do the work, because we do not know how these bytes are aligned
231 : * inside the "Datum". We assume instead that any given datatype is
232 : * consistent about how it fills extraneous bits in the Datum.
233 : */
234 1893314 : res = (value1 == value2);
235 : }
236 : else
237 : {
238 : Size size1,
239 : size2;
240 : char *s1,
241 : *s2;
242 :
243 : /*
244 : * Compare the bytes pointed by the pointers stored in the datums.
245 : */
246 801166 : size1 = datumGetSize(value1, typByVal, typLen);
247 801166 : size2 = datumGetSize(value2, typByVal, typLen);
248 801166 : if (size1 != size2)
249 1476 : return false;
250 799690 : s1 = (char *) DatumGetPointer(value1);
251 799690 : s2 = (char *) DatumGetPointer(value2);
252 799690 : res = (memcmp(s1, s2, size1) == 0);
253 : }
254 2693004 : return res;
255 : }
256 :
257 : /*-------------------------------------------------------------------------
258 : * datum_image_eq
259 : *
260 : * Compares two datums for identical contents, based on byte images. Return
261 : * true if the two datums are equal, false otherwise.
262 : *-------------------------------------------------------------------------
263 : */
264 : bool
265 7134590 : datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen)
266 : {
267 : Size len1,
268 : len2;
269 7134590 : bool result = true;
270 :
271 7134590 : if (typByVal)
272 : {
273 4894922 : result = (value1 == value2);
274 : }
275 2239668 : else if (typLen > 0)
276 : {
277 1600724 : result = (memcmp(DatumGetPointer(value1),
278 : DatumGetPointer(value2),
279 : typLen) == 0);
280 : }
281 638944 : else if (typLen == -1)
282 : {
283 285830 : len1 = toast_raw_datum_size(value1);
284 285830 : len2 = toast_raw_datum_size(value2);
285 : /* No need to de-toast if lengths don't match. */
286 285830 : if (len1 != len2)
287 23180 : result = false;
288 : else
289 : {
290 : struct varlena *arg1val;
291 : struct varlena *arg2val;
292 :
293 262650 : arg1val = PG_DETOAST_DATUM_PACKED(value1);
294 262650 : arg2val = PG_DETOAST_DATUM_PACKED(value2);
295 :
296 262650 : result = (memcmp(VARDATA_ANY(arg1val),
297 262650 : VARDATA_ANY(arg2val),
298 : len1 - VARHDRSZ) == 0);
299 :
300 : /* Only free memory if it's a copy made here. */
301 262650 : if ((Pointer) arg1val != (Pointer) value1)
302 0 : pfree(arg1val);
303 262650 : if ((Pointer) arg2val != (Pointer) value2)
304 0 : pfree(arg2val);
305 : }
306 : }
307 353114 : else if (typLen == -2)
308 : {
309 : char *s1,
310 : *s2;
311 :
312 : /* Compare cstring datums */
313 353114 : s1 = DatumGetCString(value1);
314 353114 : s2 = DatumGetCString(value2);
315 353114 : len1 = strlen(s1) + 1;
316 353114 : len2 = strlen(s2) + 1;
317 353114 : if (len1 != len2)
318 166290 : return false;
319 186824 : result = (memcmp(s1, s2, len1) == 0);
320 : }
321 : else
322 0 : elog(ERROR, "unexpected typLen: %d", typLen);
323 :
324 6968300 : return result;
325 : }
326 :
327 : /*-------------------------------------------------------------------------
328 : * btequalimage
329 : *
330 : * Generic "equalimage" support function.
331 : *
332 : * B-Tree operator classes whose equality function could safely be replaced by
333 : * datum_image_eq() in all cases can use this as their "equalimage" support
334 : * function.
335 : *
336 : * Currently, we unconditionally assume that any B-Tree operator class that
337 : * registers btequalimage as its support function 4 must be able to safely use
338 : * optimizations like deduplication (i.e. we return true unconditionally). If
339 : * it ever proved necessary to rescind support for an operator class, we could
340 : * do that in a targeted fashion by doing something with the opcintype
341 : * argument.
342 : *-------------------------------------------------------------------------
343 : */
344 : Datum
345 7484 : btequalimage(PG_FUNCTION_ARGS)
346 : {
347 : /* Oid opcintype = PG_GETARG_OID(0); */
348 :
349 7484 : PG_RETURN_BOOL(true);
350 : }
351 :
352 : /*-------------------------------------------------------------------------
353 : * datumEstimateSpace
354 : *
355 : * Compute the amount of space that datumSerialize will require for a
356 : * particular Datum.
357 : *-------------------------------------------------------------------------
358 : */
359 : Size
360 80 : datumEstimateSpace(Datum value, bool isnull, bool typByVal, int typLen)
361 : {
362 80 : Size sz = sizeof(int);
363 :
364 80 : if (!isnull)
365 : {
366 : /* no need to use add_size, can't overflow */
367 80 : if (typByVal)
368 72 : sz += sizeof(Datum);
369 8 : else if (typLen == -1 &&
370 8 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
371 : {
372 : /* Expanded objects need to be flattened, see comment below */
373 4 : sz += EOH_get_flat_size(DatumGetEOHP(value));
374 : }
375 : else
376 4 : sz += datumGetSize(value, typByVal, typLen);
377 : }
378 :
379 80 : return sz;
380 : }
381 :
382 : /*-------------------------------------------------------------------------
383 : * datumSerialize
384 : *
385 : * Serialize a possibly-NULL datum into caller-provided storage.
386 : *
387 : * Note: "expanded" objects are flattened so as to produce a self-contained
388 : * representation, but other sorts of toast pointers are transferred as-is.
389 : * This is because the intended use of this function is to pass the value
390 : * to another process within the same database server. The other process
391 : * could not access an "expanded" object within this process's memory, but
392 : * we assume it can dereference the same TOAST pointers this one can.
393 : *
394 : * The format is as follows: first, we write a 4-byte header word, which
395 : * is either the length of a pass-by-reference datum, -1 for a
396 : * pass-by-value datum, or -2 for a NULL. If the value is NULL, nothing
397 : * further is written. If it is pass-by-value, sizeof(Datum) bytes
398 : * follow. Otherwise, the number of bytes indicated by the header word
399 : * follow. The caller is responsible for ensuring that there is enough
400 : * storage to store the number of bytes that will be written; use
401 : * datumEstimateSpace() to find out how many will be needed.
402 : * *start_address is updated to point to the byte immediately following
403 : * those written.
404 : *-------------------------------------------------------------------------
405 : */
406 : void
407 80 : datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
408 : char **start_address)
409 : {
410 80 : ExpandedObjectHeader *eoh = NULL;
411 : int header;
412 :
413 : /* Write header word. */
414 80 : if (isnull)
415 0 : header = -2;
416 80 : else if (typByVal)
417 72 : header = -1;
418 8 : else if (typLen == -1 &&
419 8 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
420 : {
421 4 : eoh = DatumGetEOHP(value);
422 4 : header = EOH_get_flat_size(eoh);
423 : }
424 : else
425 4 : header = datumGetSize(value, typByVal, typLen);
426 80 : memcpy(*start_address, &header, sizeof(int));
427 80 : *start_address += sizeof(int);
428 :
429 : /* If not null, write payload bytes. */
430 80 : if (!isnull)
431 : {
432 80 : if (typByVal)
433 : {
434 72 : memcpy(*start_address, &value, sizeof(Datum));
435 72 : *start_address += sizeof(Datum);
436 : }
437 8 : else if (eoh)
438 : {
439 : char *tmp;
440 :
441 : /*
442 : * EOH_flatten_into expects the target address to be maxaligned,
443 : * so we can't store directly to *start_address.
444 : */
445 4 : tmp = (char *) palloc(header);
446 4 : EOH_flatten_into(eoh, (void *) tmp, header);
447 4 : memcpy(*start_address, tmp, header);
448 4 : *start_address += header;
449 :
450 : /* be tidy. */
451 4 : pfree(tmp);
452 : }
453 : else
454 : {
455 4 : memcpy(*start_address, DatumGetPointer(value), header);
456 4 : *start_address += header;
457 : }
458 : }
459 80 : }
460 :
461 : /*-------------------------------------------------------------------------
462 : * datumRestore
463 : *
464 : * Restore a possibly-NULL datum previously serialized by datumSerialize.
465 : * *start_address is updated according to the number of bytes consumed.
466 : *-------------------------------------------------------------------------
467 : */
468 : Datum
469 184 : datumRestore(char **start_address, bool *isnull)
470 : {
471 : int header;
472 : void *d;
473 :
474 : /* Read header word. */
475 184 : memcpy(&header, *start_address, sizeof(int));
476 184 : *start_address += sizeof(int);
477 :
478 : /* If this datum is NULL, we can stop here. */
479 184 : if (header == -2)
480 : {
481 0 : *isnull = true;
482 0 : return (Datum) 0;
483 : }
484 :
485 : /* OK, datum is not null. */
486 184 : *isnull = false;
487 :
488 : /* If this datum is pass-by-value, sizeof(Datum) bytes follow. */
489 184 : if (header == -1)
490 : {
491 : Datum val;
492 :
493 160 : memcpy(&val, *start_address, sizeof(Datum));
494 160 : *start_address += sizeof(Datum);
495 160 : return val;
496 : }
497 :
498 : /* Pass-by-reference case; copy indicated number of bytes. */
499 : Assert(header > 0);
500 24 : d = palloc(header);
501 24 : memcpy(d, *start_address, header);
502 24 : *start_address += header;
503 24 : return PointerGetDatum(d);
504 : }
|