1 /*-------------------------------------------------------------------------
4 * I/O functions for domain types.
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.
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.
22 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
23 * Portions Copyright (c) 1994, Regents of the University of California
27 * src/backend/utils/adt/domains.c
29 *-------------------------------------------------------------------------
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"
43 static bool domain_check_internal(Datum value
, bool isnull
, Oid domainType
,
44 void **extra
, MemoryContext mcxt
,
48 * structure to cache state across multiple calls
50 typedef struct DomainIOData
53 /* Data needed to call base type's input function */
58 /* Reference to cached list of constraint items to check */
59 DomainConstraintRef constraint_ref
;
60 /* Context for evaluating CHECK constraints in */
61 ExprContext
*econtext
;
62 /* Memory context this cache is in */
68 * domain_state_setup - initialize the cache for a new domain type.
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.
76 domain_state_setup(Oid domainType
, bool binary
, MemoryContext mcxt
)
78 DomainIOData
*my_extra
;
79 TypeCacheEntry
*typentry
;
82 my_extra
= (DomainIOData
*) MemoryContextAlloc(mcxt
, sizeof(DomainIOData
));
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.
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
))));
98 /* Find out the base type */
99 baseType
= typentry
->domainBaseType
;
100 my_extra
->typtypmod
= typentry
->domainBaseTypmod
;
102 /* Look up underlying I/O function */
104 getTypeBinaryInputInfo(baseType
,
105 &my_extra
->typiofunc
,
106 &my_extra
->typioparam
);
108 getTypeInputInfo(baseType
,
109 &my_extra
->typiofunc
,
110 &my_extra
->typioparam
);
111 fmgr_info_cxt(my_extra
->typiofunc
, &my_extra
->proc
, mcxt
);
113 /* Look up constraints for domain */
114 InitDomainConstraintRef(domainType
, &my_extra
->constraint_ref
, mcxt
, true);
116 /* We don't make an ExprContext until needed */
117 my_extra
->econtext
= NULL
;
118 my_extra
->mcxt
= mcxt
;
120 /* Mark cache valid */
121 my_extra
->domain_type
= domainType
;
127 * domain_check_input - apply the cached checks.
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.
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.
138 domain_check_input(Datum value
, bool isnull
, DomainIOData
*my_extra
,
141 ExprContext
*econtext
= my_extra
->econtext
;
144 /* Make sure we have up-to-date constraints */
145 UpdateDomainConstraintRef(&my_extra
->constraint_ref
);
147 foreach(l
, my_extra
->constraint_ref
.constraints
)
149 DomainConstraintState
*con
= (DomainConstraintState
*) lfirst(l
);
151 switch (con
->constrainttype
)
153 case DOM_CONSTRAINT_NOTNULL
:
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
)));
164 case DOM_CONSTRAINT_CHECK
:
166 /* Make the econtext if we didn't already */
167 if (econtext
== NULL
)
169 MemoryContext oldcontext
;
171 oldcontext
= MemoryContextSwitchTo(my_extra
->mcxt
);
172 econtext
= CreateStandaloneExprContext();
173 MemoryContextSwitchTo(oldcontext
);
174 my_extra
->econtext
= econtext
;
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
187 econtext
->domainValue_datum
=
188 MakeExpandedObjectReadOnly(value
, isnull
,
189 my_extra
->constraint_ref
.tcache
->typlen
);
190 econtext
->domainValue_isNull
= isnull
;
192 if (!ExecCheck(con
->check_exprstate
, econtext
))
195 (errcode(ERRCODE_CHECK_VIOLATION
),
196 errmsg("value for domain %s violates check constraint \"%s\"",
197 format_type_be(my_extra
->domain_type
),
199 errdomainconstraint(my_extra
->domain_type
,
206 elog(ERROR
, "unrecognized constraint type: %d",
207 (int) con
->constrainttype
);
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.
219 ReScanExprContext(econtext
);
224 * domain_in - input routine for any domain type.
227 domain_in(PG_FUNCTION_ARGS
)
231 Node
*escontext
= fcinfo
->context
;
232 DomainIOData
*my_extra
;
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.
243 string
= PG_GETARG_CSTRING(0);
246 domainType
= PG_GETARG_OID(1);
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).
253 my_extra
= (DomainIOData
*) fcinfo
->flinfo
->fn_extra
;
254 if (my_extra
== NULL
|| my_extra
->domain_type
!= domainType
)
256 my_extra
= domain_state_setup(domainType
, false,
257 fcinfo
->flinfo
->fn_mcxt
);
258 fcinfo
->flinfo
->fn_extra
= my_extra
;
262 * Invoke the base type's typinput procedure to convert the data.
264 if (!InputFunctionCallSafe(&my_extra
->proc
,
266 my_extra
->typioparam
,
273 * Do the necessary checks to ensure it's a valid domain value.
275 domain_check_input(value
, (string
== NULL
), my_extra
, escontext
);
280 PG_RETURN_DATUM(value
);
284 * domain_recv - binary input routine for any domain type.
287 domain_recv(PG_FUNCTION_ARGS
)
291 DomainIOData
*my_extra
;
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.
302 buf
= (StringInfo
) PG_GETARG_POINTER(0);
305 domainType
= PG_GETARG_OID(1);
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).
312 my_extra
= (DomainIOData
*) fcinfo
->flinfo
->fn_extra
;
313 if (my_extra
== NULL
|| my_extra
->domain_type
!= domainType
)
315 my_extra
= domain_state_setup(domainType
, true,
316 fcinfo
->flinfo
->fn_mcxt
);
317 fcinfo
->flinfo
->fn_extra
= my_extra
;
321 * Invoke the base type's typreceive procedure to convert the data.
323 value
= ReceiveFunctionCall(&my_extra
->proc
,
325 my_extra
->typioparam
,
326 my_extra
->typtypmod
);
329 * Do the necessary checks to ensure it's a valid domain value.
331 domain_check_input(value
, (buf
== NULL
), my_extra
, NULL
);
336 PG_RETURN_DATUM(value
);
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.
346 domain_check(Datum value
, bool isnull
, Oid domainType
,
347 void **extra
, MemoryContext mcxt
)
349 (void) domain_check_internal(value
, isnull
, domainType
, extra
, mcxt
,
353 /* Error-safe variant of domain_check(). */
355 domain_check_safe(Datum value
, bool isnull
, Oid domainType
,
356 void **extra
, MemoryContext mcxt
,
359 return domain_check_internal(value
, isnull
, domainType
, extra
, mcxt
,
364 * domain_check_internal
365 * Workhorse for domain_check() and domain_check_safe()
367 * Returns false if an error occurred in domain_check_input() and 'escontext'
368 * points to an ErrorSaveContext, true otherwise.
371 domain_check_internal(Datum value
, bool isnull
, Oid domainType
,
372 void **extra
, MemoryContext mcxt
,
375 DomainIOData
*my_extra
= NULL
;
378 mcxt
= CurrentMemoryContext
;
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).
386 my_extra
= (DomainIOData
*) *extra
;
387 if (my_extra
== NULL
|| my_extra
->domain_type
!= domainType
)
389 my_extra
= domain_state_setup(domainType
, true, mcxt
);
395 * Do the necessary checks to ensure it's a valid domain value.
397 domain_check_input(value
, isnull
, my_extra
, escontext
);
399 return !SOFT_ERROR_OCCURRED(escontext
);
403 * errdatatype --- stores schema_name and datatype_name of a datatype
404 * within the current errordata.
407 errdatatype(Oid datatypeOid
)
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
);
417 err_generic_string(PG_DIAG_SCHEMA_NAME
,
418 get_namespace_name(typtup
->typnamespace
));
419 err_generic_string(PG_DIAG_DATATYPE_NAME
, NameStr(typtup
->typname
));
421 ReleaseSysCache(tup
);
423 return 0; /* return value does not matter */
427 * errdomainconstraint --- stores schema_name, datatype_name and
428 * constraint_name of a domain-related constraint within the current errordata.
431 errdomainconstraint(Oid datatypeOid
, const char *conname
)
433 errdatatype(datatypeOid
);
434 err_generic_string(PG_DIAG_CONSTRAINT_NAME
, conname
);
436 return 0; /* return value does not matter */