Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * datum.c
4 : * POSTGRES Datum (abstract data type) manipulation routines.
5 : *
6 : * Portions Copyright (c) 1996-2017, 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
61 747672 : datumGetSize(Datum value, bool typByVal, int typLen)
62 : {
63 : Size size;
64 :
65 747672 : if (typByVal)
66 : {
67 : /* Pass-by-value types are always fixed-length */
68 1682 : Assert(typLen > 0 && typLen <= sizeof(Datum));
69 1682 : size = (Size) typLen;
70 : }
71 : else
72 : {
73 745990 : if (typLen > 0)
74 : {
75 : /* Fixed-length pass-by-ref type */
76 739633 : size = (Size) typLen;
77 : }
78 6357 : else if (typLen == -1)
79 : {
80 : /* It is a varlena datatype */
81 6204 : struct varlena *s = (struct varlena *) DatumGetPointer(value);
82 :
83 6204 : if (!PointerIsValid(s))
84 0 : ereport(ERROR,
85 : (errcode(ERRCODE_DATA_EXCEPTION),
86 : errmsg("invalid Datum pointer")));
87 :
88 6204 : size = (Size) VARSIZE_ANY(s);
89 : }
90 153 : else if (typLen == -2)
91 : {
92 : /* It is a cstring datatype */
93 153 : char *s = (char *) DatumGetPointer(value);
94 :
95 153 : if (!PointerIsValid(s))
96 0 : ereport(ERROR,
97 : (errcode(ERRCODE_DATA_EXCEPTION),
98 : errmsg("invalid Datum pointer")));
99 :
100 153 : size = (Size) (strlen(s) + 1);
101 : }
102 : else
103 : {
104 0 : elog(ERROR, "invalid typLen: %d", typLen);
105 : size = 0; /* keep compiler quiet */
106 : }
107 : }
108 :
109 747672 : 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
128 1774245 : datumCopy(Datum value, bool typByVal, int typLen)
129 : {
130 : Datum res;
131 :
132 1774245 : if (typByVal)
133 541279 : res = value;
134 1232966 : else if (typLen == -1)
135 : {
136 : /* It is a varlena datatype */
137 510264 : struct varlena *vl = (struct varlena *) DatumGetPointer(value);
138 :
139 510264 : if (VARATT_IS_EXTERNAL_EXPANDED(vl))
140 14 : {
141 : /* Flatten into the caller's memory context */
142 14 : ExpandedObjectHeader *eoh = DatumGetEOHP(value);
143 : Size resultsize;
144 : char *resultptr;
145 :
146 14 : resultsize = EOH_get_flat_size(eoh);
147 14 : resultptr = (char *) palloc(resultsize);
148 14 : EOH_flatten_into(eoh, (void *) resultptr, resultsize);
149 14 : res = PointerGetDatum(resultptr);
150 : }
151 : else
152 : {
153 : /* Otherwise, just copy the varlena datum verbatim */
154 : Size realSize;
155 : char *resultptr;
156 :
157 510250 : realSize = (Size) VARSIZE_ANY(vl);
158 510250 : resultptr = (char *) palloc(realSize);
159 510250 : memcpy(resultptr, vl, realSize);
160 510250 : 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 722702 : realSize = datumGetSize(value, typByVal, typLen);
170 :
171 722702 : resultptr = (char *) palloc(realSize);
172 722702 : memcpy(resultptr, DatumGetPointer(value), realSize);
173 722702 : res = PointerGetDatum(resultptr);
174 : }
175 1774245 : 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
190 3444 : datumTransfer(Datum value, bool typByVal, int typLen)
191 : {
192 6706 : if (!typByVal && typLen == -1 &&
193 3280 : VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(value)))
194 18 : value = TransferExpandedObject(value, CurrentMemoryContext);
195 : else
196 3426 : value = datumCopy(value, typByVal, typLen);
197 3444 : 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
219 32801 : datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
220 : {
221 : bool res;
222 :
223 32801 : 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 21517 : 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 11284 : size1 = datumGetSize(value1, typByVal, typLen);
244 11284 : size2 = datumGetSize(value2, typByVal, typLen);
245 11284 : if (size1 != size2)
246 250 : return false;
247 11034 : s1 = (char *) DatumGetPointer(value1);
248 11034 : s2 = (char *) DatumGetPointer(value2);
249 11034 : res = (memcmp(s1, s2, size1) == 0);
250 : }
251 32551 : 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
262 0 : datumEstimateSpace(Datum value, bool isnull, bool typByVal, int typLen)
263 : {
264 0 : Size sz = sizeof(int);
265 :
266 0 : if (!isnull)
267 : {
268 : /* no need to use add_size, can't overflow */
269 0 : if (typByVal)
270 0 : sz += sizeof(Datum);
271 0 : else if (typLen == -1 &&
272 0 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
273 : {
274 : /* Expanded objects need to be flattened, see comment below */
275 0 : sz += EOH_get_flat_size(DatumGetEOHP(value));
276 : }
277 : else
278 0 : sz += datumGetSize(value, typByVal, typLen);
279 : }
280 :
281 0 : 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
309 0 : datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
310 : char **start_address)
311 : {
312 0 : ExpandedObjectHeader *eoh = NULL;
313 : int header;
314 :
315 : /* Write header word. */
316 0 : if (isnull)
317 0 : header = -2;
318 0 : else if (typByVal)
319 0 : header = -1;
320 0 : else if (typLen == -1 &&
321 0 : VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
322 : {
323 0 : eoh = DatumGetEOHP(value);
324 0 : header = EOH_get_flat_size(eoh);
325 : }
326 : else
327 0 : header = datumGetSize(value, typByVal, typLen);
328 0 : memcpy(*start_address, &header, sizeof(int));
329 0 : *start_address += sizeof(int);
330 :
331 : /* If not null, write payload bytes. */
332 0 : if (!isnull)
333 : {
334 0 : if (typByVal)
335 : {
336 0 : memcpy(*start_address, &value, sizeof(Datum));
337 0 : *start_address += sizeof(Datum);
338 : }
339 0 : else if (eoh)
340 : {
341 0 : EOH_flatten_into(eoh, (void *) *start_address, header);
342 0 : *start_address += header;
343 : }
344 : else
345 : {
346 0 : memcpy(*start_address, DatumGetPointer(value), header);
347 0 : *start_address += header;
348 : }
349 : }
350 0 : }
351 :
352 : /*-------------------------------------------------------------------------
353 : * datumRestore
354 : *
355 : * Restore a possibly-NULL datum previously serialized by datumSerialize.
356 : * *start_address is updated according to the number of bytes consumed.
357 : *-------------------------------------------------------------------------
358 : */
359 : Datum
360 0 : datumRestore(char **start_address, bool *isnull)
361 : {
362 : int header;
363 : void *d;
364 :
365 : /* Read header word. */
366 0 : memcpy(&header, *start_address, sizeof(int));
367 0 : *start_address += sizeof(int);
368 :
369 : /* If this datum is NULL, we can stop here. */
370 0 : if (header == -2)
371 : {
372 0 : *isnull = true;
373 0 : return (Datum) 0;
374 : }
375 :
376 : /* OK, datum is not null. */
377 0 : *isnull = false;
378 :
379 : /* If this datum is pass-by-value, sizeof(Datum) bytes follow. */
380 0 : if (header == -1)
381 : {
382 : Datum val;
383 :
384 0 : memcpy(&val, *start_address, sizeof(Datum));
385 0 : *start_address += sizeof(Datum);
386 0 : return val;
387 : }
388 :
389 : /* Pass-by-reference case; copy indicated number of bytes. */
390 0 : Assert(header > 0);
391 0 : d = palloc(header);
392 0 : memcpy(d, *start_address, header);
393 0 : *start_address += header;
394 0 : return PointerGetDatum(d);
395 : }
|