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