PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
domains.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * domains.c
4 * I/O functions for domain types.
5 *
6 * The output functions for a domain type are just the same ones provided
7 * by its underlying base type. The input functions, however, must be
8 * prepared to apply any constraints defined by the type. So, we create
9 * special input functions that invoke the base type's input function
10 * and then check the constraints.
11 *
12 * The overhead required for constraint checking can be high, since examining
13 * the catalogs to discover the constraints for a given domain is not cheap.
14 * We have three mechanisms for minimizing this cost:
15 * 1. We rely on the typcache to keep up-to-date copies of the constraints.
16 * 2. In a nest of domains, we flatten the checking of all the levels
17 * into just one operation (the typcache does this for us).
18 * 3. If there are CHECK constraints, we cache a standalone ExprContext
19 * to evaluate them in.
20 *
21 *
22 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
23 * Portions Copyright (c) 1994, Regents of the University of California
24 *
25 *
26 * IDENTIFICATION
27 * src/backend/utils/adt/domains.c
28 *
29 *-------------------------------------------------------------------------
30 */
31#include "postgres.h"
32
33#include "access/htup_details.h"
34#include "catalog/pg_type.h"
35#include "executor/executor.h"
36#include "lib/stringinfo.h"
37#include "utils/builtins.h"
38#include "utils/expandeddatum.h"
39#include "utils/lsyscache.h"
40#include "utils/syscache.h"
41#include "utils/typcache.h"
42
43static bool domain_check_internal(Datum value, bool isnull, Oid domainType,
44 void **extra, MemoryContext mcxt,
45 Node *escontext);
46
47/*
48 * structure to cache state across multiple calls
49 */
50typedef struct DomainIOData
51{
53 /* Data needed to call base type's input function */
58 /* Reference to cached list of constraint items to check */
60 /* Context for evaluating CHECK constraints in */
62 /* Memory context this cache is in */
65
66
67/*
68 * domain_state_setup - initialize the cache for a new domain type.
69 *
70 * Note: we can't re-use the same cache struct for a new domain type,
71 * since there's no provision for releasing the DomainConstraintRef.
72 * If a call site needs to deal with a new domain type, we just leak
73 * the old struct for the duration of the query.
74 */
75static DomainIOData *
76domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
77{
78 DomainIOData *my_extra;
79 TypeCacheEntry *typentry;
80 Oid baseType;
81
82 my_extra = (DomainIOData *) MemoryContextAlloc(mcxt, sizeof(DomainIOData));
83
84 /*
85 * Verify that domainType represents a valid domain type. We need to be
86 * careful here because domain_in and domain_recv can be called from SQL,
87 * possibly with incorrect arguments. We use lookup_type_cache mainly
88 * because it will throw a clean user-facing error for a bad OID; but also
89 * it can cache the underlying base type info.
90 */
91 typentry = lookup_type_cache(domainType, TYPECACHE_DOMAIN_BASE_INFO);
92 if (typentry->typtype != TYPTYPE_DOMAIN)
94 (errcode(ERRCODE_DATATYPE_MISMATCH),
95 errmsg("type %s is not a domain",
96 format_type_be(domainType))));
97
98 /* Find out the base type */
99 baseType = typentry->domainBaseType;
100 my_extra->typtypmod = typentry->domainBaseTypmod;
101
102 /* Look up underlying I/O function */
103 if (binary)
104 getTypeBinaryInputInfo(baseType,
105 &my_extra->typiofunc,
106 &my_extra->typioparam);
107 else
108 getTypeInputInfo(baseType,
109 &my_extra->typiofunc,
110 &my_extra->typioparam);
111 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
112
113 /* Look up constraints for domain */
114 InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt, true);
115
116 /* We don't make an ExprContext until needed */
117 my_extra->econtext = NULL;
118 my_extra->mcxt = mcxt;
119
120 /* Mark cache valid */
121 my_extra->domain_type = domainType;
122
123 return my_extra;
124}
125
126/*
127 * domain_check_input - apply the cached checks.
128 *
129 * This is roughly similar to the handling of CoerceToDomain nodes in
130 * execExpr*.c, but we execute each constraint separately, rather than
131 * compiling them in-line within a larger expression.
132 *
133 * If escontext points to an ErrorSaveContext, any failures are reported
134 * there, otherwise they are ereport'ed. Note that we do not attempt to do
135 * soft reporting of errors raised during execution of CHECK constraints.
136 */
137static void
139 Node *escontext)
140{
141 ExprContext *econtext = my_extra->econtext;
142 ListCell *l;
143
144 /* Make sure we have up-to-date constraints */
146
147 foreach(l, my_extra->constraint_ref.constraints)
148 {
150
151 switch (con->constrainttype)
152 {
154 if (isnull)
155 {
156 errsave(escontext,
157 (errcode(ERRCODE_NOT_NULL_VIOLATION),
158 errmsg("domain %s does not allow null values",
159 format_type_be(my_extra->domain_type)),
160 errdatatype(my_extra->domain_type)));
161 goto fail;
162 }
163 break;
165 {
166 /* Make the econtext if we didn't already */
167 if (econtext == NULL)
168 {
169 MemoryContext oldcontext;
170
171 oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
172 econtext = CreateStandaloneExprContext();
173 MemoryContextSwitchTo(oldcontext);
174 my_extra->econtext = econtext;
175 }
176
177 /*
178 * Set up value to be returned by CoerceToDomainValue
179 * nodes. Unlike in the generic expression case, this
180 * econtext couldn't be shared with anything else, so no
181 * need to save and restore fields. But we do need to
182 * protect the passed-in value against being changed by
183 * called functions. (It couldn't be a R/W expanded
184 * object for most uses, but that seems possible for
185 * domain_check().)
186 */
187 econtext->domainValue_datum =
189 my_extra->constraint_ref.tcache->typlen);
190 econtext->domainValue_isNull = isnull;
191
192 if (!ExecCheck(con->check_exprstate, econtext))
193 {
194 errsave(escontext,
195 (errcode(ERRCODE_CHECK_VIOLATION),
196 errmsg("value for domain %s violates check constraint \"%s\"",
197 format_type_be(my_extra->domain_type),
198 con->name),
200 con->name)));
201 goto fail;
202 }
203 break;
204 }
205 default:
206 elog(ERROR, "unrecognized constraint type: %d",
207 (int) con->constrainttype);
208 break;
209 }
210 }
211
212 /*
213 * Before exiting, call any shutdown callbacks and reset econtext's
214 * per-tuple memory. This avoids leaking non-memory resources, if
215 * anything in the expression(s) has any.
216 */
217fail:
218 if (econtext)
219 ReScanExprContext(econtext);
220}
221
222
223/*
224 * domain_in - input routine for any domain type.
225 */
226Datum
228{
229 char *string;
230 Oid domainType;
231 Node *escontext = fcinfo->context;
232 DomainIOData *my_extra;
233 Datum value;
234
235 /*
236 * Since domain_in is not strict, we have to check for null inputs. The
237 * typioparam argument should never be null in normal system usage, but it
238 * could be null in a manual invocation --- if so, just return null.
239 */
240 if (PG_ARGISNULL(0))
241 string = NULL;
242 else
243 string = PG_GETARG_CSTRING(0);
244 if (PG_ARGISNULL(1))
246 domainType = PG_GETARG_OID(1);
247
248 /*
249 * We arrange to look up the needed info just once per series of calls,
250 * assuming the domain type doesn't change underneath us (which really
251 * shouldn't happen, but cope if it does).
252 */
253 my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
254 if (my_extra == NULL || my_extra->domain_type != domainType)
255 {
256 my_extra = domain_state_setup(domainType, false,
257 fcinfo->flinfo->fn_mcxt);
258 fcinfo->flinfo->fn_extra = my_extra;
259 }
260
261 /*
262 * Invoke the base type's typinput procedure to convert the data.
263 */
264 if (!InputFunctionCallSafe(&my_extra->proc,
265 string,
266 my_extra->typioparam,
267 my_extra->typtypmod,
268 escontext,
269 &value))
271
272 /*
273 * Do the necessary checks to ensure it's a valid domain value.
274 */
275 domain_check_input(value, (string == NULL), my_extra, escontext);
276
277 if (string == NULL)
279 else
281}
282
283/*
284 * domain_recv - binary input routine for any domain type.
285 */
286Datum
288{
290 Oid domainType;
291 DomainIOData *my_extra;
292 Datum value;
293
294 /*
295 * Since domain_recv is not strict, we have to check for null inputs. The
296 * typioparam argument should never be null in normal system usage, but it
297 * could be null in a manual invocation --- if so, just return null.
298 */
299 if (PG_ARGISNULL(0))
300 buf = NULL;
301 else
303 if (PG_ARGISNULL(1))
305 domainType = PG_GETARG_OID(1);
306
307 /*
308 * We arrange to look up the needed info just once per series of calls,
309 * assuming the domain type doesn't change underneath us (which really
310 * shouldn't happen, but cope if it does).
311 */
312 my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
313 if (my_extra == NULL || my_extra->domain_type != domainType)
314 {
315 my_extra = domain_state_setup(domainType, true,
316 fcinfo->flinfo->fn_mcxt);
317 fcinfo->flinfo->fn_extra = my_extra;
318 }
319
320 /*
321 * Invoke the base type's typreceive procedure to convert the data.
322 */
323 value = ReceiveFunctionCall(&my_extra->proc,
324 buf,
325 my_extra->typioparam,
326 my_extra->typtypmod);
327
328 /*
329 * Do the necessary checks to ensure it's a valid domain value.
330 */
331 domain_check_input(value, (buf == NULL), my_extra, NULL);
332
333 if (buf == NULL)
335 else
337}
338
339/*
340 * domain_check - check that a datum satisfies the constraints of a
341 * domain. extra and mcxt can be passed if they are available from,
342 * say, a FmgrInfo structure, or they can be NULL, in which case the
343 * setup is repeated for each call.
344 */
345void
346domain_check(Datum value, bool isnull, Oid domainType,
347 void **extra, MemoryContext mcxt)
348{
349 (void) domain_check_internal(value, isnull, domainType, extra, mcxt,
350 NULL);
351}
352
353/* Error-safe variant of domain_check(). */
354bool
355domain_check_safe(Datum value, bool isnull, Oid domainType,
356 void **extra, MemoryContext mcxt,
357 Node *escontext)
358{
359 return domain_check_internal(value, isnull, domainType, extra, mcxt,
360 escontext);
361}
362
363/*
364 * domain_check_internal
365 * Workhorse for domain_check() and domain_check_safe()
366 *
367 * Returns false if an error occurred in domain_check_input() and 'escontext'
368 * points to an ErrorSaveContext, true otherwise.
369 */
370static bool
371domain_check_internal(Datum value, bool isnull, Oid domainType,
372 void **extra, MemoryContext mcxt,
373 Node *escontext)
374{
375 DomainIOData *my_extra = NULL;
376
377 if (mcxt == NULL)
379
380 /*
381 * We arrange to look up the needed info just once per series of calls,
382 * assuming the domain type doesn't change underneath us (which really
383 * shouldn't happen, but cope if it does).
384 */
385 if (extra)
386 my_extra = (DomainIOData *) *extra;
387 if (my_extra == NULL || my_extra->domain_type != domainType)
388 {
389 my_extra = domain_state_setup(domainType, true, mcxt);
390 if (extra)
391 *extra = my_extra;
392 }
393
394 /*
395 * Do the necessary checks to ensure it's a valid domain value.
396 */
397 domain_check_input(value, isnull, my_extra, escontext);
398
399 return !SOFT_ERROR_OCCURRED(escontext);
400}
401
402/*
403 * errdatatype --- stores schema_name and datatype_name of a datatype
404 * within the current errordata.
405 */
406int
407errdatatype(Oid datatypeOid)
408{
409 HeapTuple tup;
410 Form_pg_type typtup;
411
412 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(datatypeOid));
413 if (!HeapTupleIsValid(tup))
414 elog(ERROR, "cache lookup failed for type %u", datatypeOid);
415 typtup = (Form_pg_type) GETSTRUCT(tup);
416
418 get_namespace_name(typtup->typnamespace));
420
421 ReleaseSysCache(tup);
422
423 return 0; /* return value does not matter */
424}
425
426/*
427 * errdomainconstraint --- stores schema_name, datatype_name and
428 * constraint_name of a domain-related constraint within the current errordata.
429 */
430int
431errdomainconstraint(Oid datatypeOid, const char *conname)
432{
433 errdatatype(datatypeOid);
435
436 return 0; /* return value does not matter */
437}
#define NameStr(name)
Definition: c.h:717
int32_t int32
Definition: c.h:498
static bool domain_check_internal(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt, Node *escontext)
Definition: domains.c:371
static DomainIOData * domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
Definition: domains.c:76
struct DomainIOData DomainIOData
int errdatatype(Oid datatypeOid)
Definition: domains.c:407
Datum domain_recv(PG_FUNCTION_ARGS)
Definition: domains.c:287
int errdomainconstraint(Oid datatypeOid, const char *conname)
Definition: domains.c:431
Datum domain_in(PG_FUNCTION_ARGS)
Definition: domains.c:227
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
Definition: domains.c:346
static void domain_check_input(Datum value, bool isnull, DomainIOData *my_extra, Node *escontext)
Definition: domains.c:138
bool domain_check_safe(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt, Node *escontext)
Definition: domains.c:355
int err_generic_string(int field, const char *str)
Definition: elog.c:1534
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define errsave(context,...)
Definition: elog.h:262
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:149
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:872
void ReScanExprContext(ExprContext *econtext)
Definition: execUtils.c:444
ExprContext * CreateStandaloneExprContext(void)
Definition: execUtils.c:358
@ DOM_CONSTRAINT_CHECK
Definition: execnodes.h:1044
@ DOM_CONSTRAINT_NOTNULL
Definition: execnodes.h:1043
#define MakeExpandedObjectReadOnly(d, isnull, typlen)
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod, fmNodePtr escontext, Datum *result)
Definition: fmgr.c:1585
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1697
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define PG_ARGISNULL(n)
Definition: fmgr.h:209
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:277
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
static struct @165 value
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:3014
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3506
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition: lsyscache.c:3080
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1256
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
#define SOFT_ERROR_OCCURRED(escontext)
Definition: miscnodes.h:53
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define lfirst(lc)
Definition: pg_list.h:172
static char * buf
Definition: pg_test_fsync.c:72
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
uintptr_t Datum
Definition: postgres.h:69
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
#define PG_DIAG_SCHEMA_NAME
Definition: postgres_ext.h:60
#define PG_DIAG_CONSTRAINT_NAME
Definition: postgres_ext.h:64
#define PG_DIAG_DATATYPE_NAME
Definition: postgres_ext.h:63
unsigned int Oid
Definition: postgres_ext.h:30
char string[11]
Definition: preproc-type.c:52
StringInfoData * StringInfo
Definition: stringinfo.h:54
TypeCacheEntry * tcache
Definition: typcache.h:169
DomainConstraintType constrainttype
Definition: execnodes.h:1050
ExprState * check_exprstate
Definition: execnodes.h:1053
int32 typtypmod
Definition: domains.c:56
Oid domain_type
Definition: domains.c:52
DomainConstraintRef constraint_ref
Definition: domains.c:59
FmgrInfo proc
Definition: domains.c:57
ExprContext * econtext
Definition: domains.c:61
MemoryContext mcxt
Definition: domains.c:63
Oid typiofunc
Definition: domains.c:54
Oid typioparam
Definition: domains.c:55
Datum domainValue_datum
Definition: execnodes.h:299
bool domainValue_isNull
Definition: execnodes.h:301
Definition: fmgr.h:57
Definition: nodes.h:135
int32 domainBaseTypmod
Definition: typcache.h:116
char typtype
Definition: typcache.h:43
int16 typlen
Definition: typcache.h:39
Oid domainBaseType
Definition: typcache.h:115
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx, bool need_exprstate)
Definition: typcache.c:1402
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition: typcache.c:1440
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:386
#define TYPECACHE_DOMAIN_BASE_INFO
Definition: typcache.h:150