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