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-2022, 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"
45 * structure to cache state across multiple calls
47 typedef struct DomainIOData
50 /* Data needed to call base type's input function */
55 /* Reference to cached list of constraint items to check */
56 DomainConstraintRef constraint_ref
;
57 /* Context for evaluating CHECK constraints in */
58 ExprContext
*econtext
;
59 /* Memory context this cache is in */
65 * domain_state_setup - initialize the cache for a new domain type.
67 * Note: we can't re-use the same cache struct for a new domain type,
68 * since there's no provision for releasing the DomainConstraintRef.
69 * If a call site needs to deal with a new domain type, we just leak
70 * the old struct for the duration of the query.
73 domain_state_setup(Oid domainType
, bool binary
, MemoryContext mcxt
)
75 DomainIOData
*my_extra
;
76 TypeCacheEntry
*typentry
;
79 my_extra
= (DomainIOData
*) MemoryContextAlloc(mcxt
, sizeof(DomainIOData
));
82 * Verify that domainType represents a valid domain type. We need to be
83 * careful here because domain_in and domain_recv can be called from SQL,
84 * possibly with incorrect arguments. We use lookup_type_cache mainly
85 * because it will throw a clean user-facing error for a bad OID; but also
86 * it can cache the underlying base type info.
88 typentry
= lookup_type_cache(domainType
, TYPECACHE_DOMAIN_BASE_INFO
);
89 if (typentry
->typtype
!= TYPTYPE_DOMAIN
)
91 (errcode(ERRCODE_DATATYPE_MISMATCH
),
92 errmsg("type %s is not a domain",
93 format_type_be(domainType
))));
95 /* Find out the base type */
96 baseType
= typentry
->domainBaseType
;
97 my_extra
->typtypmod
= typentry
->domainBaseTypmod
;
99 /* Look up underlying I/O function */
101 getTypeBinaryInputInfo(baseType
,
102 &my_extra
->typiofunc
,
103 &my_extra
->typioparam
);
105 getTypeInputInfo(baseType
,
106 &my_extra
->typiofunc
,
107 &my_extra
->typioparam
);
108 fmgr_info_cxt(my_extra
->typiofunc
, &my_extra
->proc
, mcxt
);
110 /* Look up constraints for domain */
111 InitDomainConstraintRef(domainType
, &my_extra
->constraint_ref
, mcxt
, true);
113 /* We don't make an ExprContext until needed */
114 my_extra
->econtext
= NULL
;
115 my_extra
->mcxt
= mcxt
;
117 /* Mark cache valid */
118 my_extra
->domain_type
= domainType
;
124 * domain_check_input - apply the cached checks.
126 * This is roughly similar to the handling of CoerceToDomain nodes in
127 * execExpr*.c, but we execute each constraint separately, rather than
128 * compiling them in-line within a larger expression.
131 domain_check_input(Datum value
, bool isnull
, DomainIOData
*my_extra
)
133 ExprContext
*econtext
= my_extra
->econtext
;
136 /* Make sure we have up-to-date constraints */
137 UpdateDomainConstraintRef(&my_extra
->constraint_ref
);
139 foreach(l
, my_extra
->constraint_ref
.constraints
)
141 DomainConstraintState
*con
= (DomainConstraintState
*) lfirst(l
);
143 switch (con
->constrainttype
)
145 case DOM_CONSTRAINT_NOTNULL
:
148 (errcode(ERRCODE_NOT_NULL_VIOLATION
),
149 errmsg("domain %s does not allow null values",
150 format_type_be(my_extra
->domain_type
)),
151 errdatatype(my_extra
->domain_type
)));
153 case DOM_CONSTRAINT_CHECK
:
155 /* Make the econtext if we didn't already */
156 if (econtext
== NULL
)
158 MemoryContext oldcontext
;
160 oldcontext
= MemoryContextSwitchTo(my_extra
->mcxt
);
161 econtext
= CreateStandaloneExprContext();
162 MemoryContextSwitchTo(oldcontext
);
163 my_extra
->econtext
= econtext
;
167 * Set up value to be returned by CoerceToDomainValue
168 * nodes. Unlike in the generic expression case, this
169 * econtext couldn't be shared with anything else, so no
170 * need to save and restore fields. But we do need to
171 * protect the passed-in value against being changed by
172 * called functions. (It couldn't be a R/W expanded
173 * object for most uses, but that seems possible for
176 econtext
->domainValue_datum
=
177 MakeExpandedObjectReadOnly(value
, isnull
,
178 my_extra
->constraint_ref
.tcache
->typlen
);
179 econtext
->domainValue_isNull
= isnull
;
181 if (!ExecCheck(con
->check_exprstate
, econtext
))
183 (errcode(ERRCODE_CHECK_VIOLATION
),
184 errmsg("value for domain %s violates check constraint \"%s\"",
185 format_type_be(my_extra
->domain_type
),
187 errdomainconstraint(my_extra
->domain_type
,
192 elog(ERROR
, "unrecognized constraint type: %d",
193 (int) con
->constrainttype
);
199 * Before exiting, call any shutdown callbacks and reset econtext's
200 * per-tuple memory. This avoids leaking non-memory resources, if
201 * anything in the expression(s) has any.
204 ReScanExprContext(econtext
);
209 * domain_in - input routine for any domain type.
212 domain_in(PG_FUNCTION_ARGS
)
216 DomainIOData
*my_extra
;
220 * Since domain_in is not strict, we have to check for null inputs. The
221 * typioparam argument should never be null in normal system usage, but it
222 * could be null in a manual invocation --- if so, just return null.
227 string
= PG_GETARG_CSTRING(0);
230 domainType
= PG_GETARG_OID(1);
233 * We arrange to look up the needed info just once per series of calls,
234 * assuming the domain type doesn't change underneath us (which really
235 * shouldn't happen, but cope if it does).
237 my_extra
= (DomainIOData
*) fcinfo
->flinfo
->fn_extra
;
238 if (my_extra
== NULL
|| my_extra
->domain_type
!= domainType
)
240 my_extra
= domain_state_setup(domainType
, false,
241 fcinfo
->flinfo
->fn_mcxt
);
242 fcinfo
->flinfo
->fn_extra
= (void *) my_extra
;
246 * Invoke the base type's typinput procedure to convert the data.
248 value
= InputFunctionCall(&my_extra
->proc
,
250 my_extra
->typioparam
,
251 my_extra
->typtypmod
);
254 * Do the necessary checks to ensure it's a valid domain value.
256 domain_check_input(value
, (string
== NULL
), my_extra
);
261 PG_RETURN_DATUM(value
);
265 * domain_recv - binary input routine for any domain type.
268 domain_recv(PG_FUNCTION_ARGS
)
272 DomainIOData
*my_extra
;
276 * Since domain_recv is not strict, we have to check for null inputs. The
277 * typioparam argument should never be null in normal system usage, but it
278 * could be null in a manual invocation --- if so, just return null.
283 buf
= (StringInfo
) PG_GETARG_POINTER(0);
286 domainType
= PG_GETARG_OID(1);
289 * We arrange to look up the needed info just once per series of calls,
290 * assuming the domain type doesn't change underneath us (which really
291 * shouldn't happen, but cope if it does).
293 my_extra
= (DomainIOData
*) fcinfo
->flinfo
->fn_extra
;
294 if (my_extra
== NULL
|| my_extra
->domain_type
!= domainType
)
296 my_extra
= domain_state_setup(domainType
, true,
297 fcinfo
->flinfo
->fn_mcxt
);
298 fcinfo
->flinfo
->fn_extra
= (void *) my_extra
;
302 * Invoke the base type's typreceive procedure to convert the data.
304 value
= ReceiveFunctionCall(&my_extra
->proc
,
306 my_extra
->typioparam
,
307 my_extra
->typtypmod
);
310 * Do the necessary checks to ensure it's a valid domain value.
312 domain_check_input(value
, (buf
== NULL
), my_extra
);
317 PG_RETURN_DATUM(value
);
321 * domain_check - check that a datum satisfies the constraints of a
322 * domain. extra and mcxt can be passed if they are available from,
323 * say, a FmgrInfo structure, or they can be NULL, in which case the
324 * setup is repeated for each call.
327 domain_check(Datum value
, bool isnull
, Oid domainType
,
328 void **extra
, MemoryContext mcxt
)
330 DomainIOData
*my_extra
= NULL
;
333 mcxt
= CurrentMemoryContext
;
336 * We arrange to look up the needed info just once per series of calls,
337 * assuming the domain type doesn't change underneath us (which really
338 * shouldn't happen, but cope if it does).
341 my_extra
= (DomainIOData
*) *extra
;
342 if (my_extra
== NULL
|| my_extra
->domain_type
!= domainType
)
344 my_extra
= domain_state_setup(domainType
, true, mcxt
);
346 *extra
= (void *) my_extra
;
350 * Do the necessary checks to ensure it's a valid domain value.
352 domain_check_input(value
, isnull
, my_extra
);
356 * errdatatype --- stores schema_name and datatype_name of a datatype
357 * within the current errordata.
360 errdatatype(Oid datatypeOid
)
365 tup
= SearchSysCache1(TYPEOID
, ObjectIdGetDatum(datatypeOid
));
366 if (!HeapTupleIsValid(tup
))
367 elog(ERROR
, "cache lookup failed for type %u", datatypeOid
);
368 typtup
= (Form_pg_type
) GETSTRUCT(tup
);
370 err_generic_string(PG_DIAG_SCHEMA_NAME
,
371 get_namespace_name(typtup
->typnamespace
));
372 err_generic_string(PG_DIAG_DATATYPE_NAME
, NameStr(typtup
->typname
));
374 ReleaseSysCache(tup
);
376 return 0; /* return value does not matter */
380 * errdomainconstraint --- stores schema_name, datatype_name and
381 * constraint_name of a domain-related constraint within the current errordata.
384 errdomainconstraint(Oid datatypeOid
, const char *conname
)
386 errdatatype(datatypeOid
);
387 err_generic_string(PG_DIAG_CONSTRAINT_NAME
, conname
);
389 return 0; /* return value does not matter */