summaryrefslogtreecommitdiffstats
path: root/src/include/utils/expandeddatum.h
blob: be4970fa9cacd8e9b01031748ec12842046b61a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*-------------------------------------------------------------------------
 *
 * expandeddatum.h
 *	  Declarations for access to "expanded" value representations.
 *
 * Complex data types, particularly container types such as arrays and
 * records, usually have on-disk representations that are compact but not
 * especially convenient to modify.  What's more, when we do modify them,
 * having to recopy all the rest of the value can be extremely inefficient.
 * Therefore, we provide a notion of an "expanded" representation that is used
 * only in memory and is optimized more for computation than storage.
 * The format appearing on disk is called the data type's "flattened"
 * representation, since it is required to be a contiguous blob of bytes --
 * but the type can have an expanded representation that is not.  Data types
 * must provide means to translate an expanded representation back to
 * flattened form.
 *
 * An expanded object is meant to survive across multiple operations, but
 * not to be enormously long-lived; for example it might be a local variable
 * in a PL/pgSQL procedure.  So its extra bulk compared to the on-disk format
 * is a worthwhile trade-off.
 *
 * References to expanded objects are a type of TOAST pointer.
 * Because of longstanding conventions in Postgres, this means that the
 * flattened form of such an object must always be a varlena object.
 * Fortunately that's no restriction in practice.
 *
 * There are actually two kinds of TOAST pointers for expanded objects:
 * read-only and read-write pointers.  Possession of one of the latter
 * authorizes a function to modify the value in-place rather than copying it
 * as would normally be required.  Functions should always return a read-write
 * pointer to any new expanded object they create.  Functions that modify an
 * argument value in-place must take care that they do not corrupt the old
 * value if they fail partway through.
 *
 *
 * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/expandeddatum.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef EXPANDEDDATUM_H
#define EXPANDEDDATUM_H

/* Size of an EXTERNAL datum that contains a pointer to an expanded object */
#define EXPANDED_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_expanded))

/*
 * "Methods" that must be provided for any expanded object.
 *
 * get_flat_size: compute space needed for flattened representation (total,
 * including header).
 *
 * flatten_into: construct flattened representation in the caller-allocated
 * space at *result, of size allocated_size (which will always be the result
 * of a preceding get_flat_size call; it's passed for cross-checking).
 *
 * The flattened representation must be a valid in-line, non-compressed,
 * 4-byte-header varlena object.
 *
 * Note: construction of a heap tuple from an expanded datum calls
 * get_flat_size twice, so it's worthwhile to make sure that that doesn't
 * incur too much overhead.
 */
typedef Size (*EOM_get_flat_size_method) (ExpandedObjectHeader *eohptr);
typedef void (*EOM_flatten_into_method) (ExpandedObjectHeader *eohptr,
										 void *result, Size allocated_size);

/* Struct of function pointers for an expanded object's methods */
typedef struct ExpandedObjectMethods
{
	EOM_get_flat_size_method get_flat_size;
	EOM_flatten_into_method flatten_into;
} ExpandedObjectMethods;

/*
 * Every expanded object must contain this header; typically the header
 * is embedded in some larger struct that adds type-specific fields.
 *
 * It is presumed that the header object and all subsidiary data are stored
 * in eoh_context, so that the object can be freed by deleting that context,
 * or its storage lifespan can be altered by reparenting the context.
 * (In principle the object could own additional resources, such as malloc'd
 * storage, and use a memory context reset callback to free them upon reset or
 * deletion of eoh_context.)
 *
 * We set up two TOAST pointers within the standard header, one read-write
 * and one read-only.  This allows functions to return either kind of pointer
 * without making an additional allocation, and in particular without worrying
 * whether a separately palloc'd object would have sufficient lifespan.
 * But note that these pointers are just a convenience; a pointer object
 * appearing somewhere else would still be legal.
 *
 * The typedef declaration for this appears in postgres.h.
 */
struct ExpandedObjectHeader
{
	/* Phony varlena header */
	int32		vl_len_;		/* always EOH_HEADER_MAGIC, see below */

	/* Pointer to methods required for object type */
	const ExpandedObjectMethods *eoh_methods;

	/* Memory context containing this header and subsidiary data */
	MemoryContext eoh_context;

	/* Standard R/W TOAST pointer for this object is kept here */
	char		eoh_rw_ptr[EXPANDED_POINTER_SIZE];

	/* Standard R/O TOAST pointer for this object is kept here */
	char		eoh_ro_ptr[EXPANDED_POINTER_SIZE];
};

/*
 * Particularly for read-only functions, it is handy to be able to work with
 * either regular "flat" varlena inputs or expanded inputs of the same data
 * type.  To allow determining which case an argument-fetching function has
 * returned, the first int32 of an ExpandedObjectHeader always contains -1
 * (EOH_HEADER_MAGIC to the code).  This works since no 4-byte-header varlena
 * could have that as its first 4 bytes.  Caution: we could not reliably tell
 * the difference between an ExpandedObjectHeader and a short-header object
 * with this trick.  However, it works fine if the argument fetching code
 * always returns either a 4-byte-header flat object or an expanded object.
 */
#define EOH_HEADER_MAGIC (-1)
#define VARATT_IS_EXPANDED_HEADER(PTR) \
	(((varattrib_4b *) (PTR))->va_4byte.va_header == (uint32) EOH_HEADER_MAGIC)

/*
 * Generic support functions for expanded objects.
 * (More of these might be worth inlining later.)
 */

#define EOHPGetRWDatum(eohptr)	PointerGetDatum((eohptr)->eoh_rw_ptr)
#define EOHPGetRODatum(eohptr)	PointerGetDatum((eohptr)->eoh_ro_ptr)

/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
	(((isnull) || (typlen) != -1) ? false : \
	 VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))

#define MakeExpandedObjectReadOnly(d, isnull, typlen) \
	(((isnull) || (typlen) != -1) ? (d) : \
	 MakeExpandedObjectReadOnlyInternal(d))

extern ExpandedObjectHeader *DatumGetEOHP(Datum d);
extern void EOH_init_header(ExpandedObjectHeader *eohptr,
							const ExpandedObjectMethods *methods,
							MemoryContext obj_context);
extern Size EOH_get_flat_size(ExpandedObjectHeader *eohptr);
extern void EOH_flatten_into(ExpandedObjectHeader *eohptr,
							 void *result, Size allocated_size);
extern Datum MakeExpandedObjectReadOnlyInternal(Datum d);
extern Datum TransferExpandedObject(Datum d, MemoryContext new_parent);
extern void DeleteExpandedObject(Datum d);

#endif							/* EXPANDEDDATUM_H */