Update copyright for 2022
[pgsql.git] / src / backend / utils / adt / domains.c
blob3de0cb01a2c85a651303144c390900f232bc01ad
1 /*-------------------------------------------------------------------------
3 * domains.c
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
26 * IDENTIFICATION
27 * src/backend/utils/adt/domains.c
29 *-------------------------------------------------------------------------
31 #include "postgres.h"
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
49 Oid domain_type;
50 /* Data needed to call base type's input function */
51 Oid typiofunc;
52 Oid typioparam;
53 int32 typtypmod;
54 FmgrInfo proc;
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 */
60 MemoryContext mcxt;
61 } DomainIOData;
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.
72 static DomainIOData *
73 domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
75 DomainIOData *my_extra;
76 TypeCacheEntry *typentry;
77 Oid baseType;
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)
90 ereport(ERROR,
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 */
100 if (binary)
101 getTypeBinaryInputInfo(baseType,
102 &my_extra->typiofunc,
103 &my_extra->typioparam);
104 else
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;
120 return my_extra;
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.
130 static void
131 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
133 ExprContext *econtext = my_extra->econtext;
134 ListCell *l;
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:
146 if (isnull)
147 ereport(ERROR,
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)));
152 break;
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
174 * domain_check().)
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))
182 ereport(ERROR,
183 (errcode(ERRCODE_CHECK_VIOLATION),
184 errmsg("value for domain %s violates check constraint \"%s\"",
185 format_type_be(my_extra->domain_type),
186 con->name),
187 errdomainconstraint(my_extra->domain_type,
188 con->name)));
189 break;
191 default:
192 elog(ERROR, "unrecognized constraint type: %d",
193 (int) con->constrainttype);
194 break;
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.
203 if (econtext)
204 ReScanExprContext(econtext);
209 * domain_in - input routine for any domain type.
211 Datum
212 domain_in(PG_FUNCTION_ARGS)
214 char *string;
215 Oid domainType;
216 DomainIOData *my_extra;
217 Datum value;
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.
224 if (PG_ARGISNULL(0))
225 string = NULL;
226 else
227 string = PG_GETARG_CSTRING(0);
228 if (PG_ARGISNULL(1))
229 PG_RETURN_NULL();
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,
249 string,
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);
258 if (string == NULL)
259 PG_RETURN_NULL();
260 else
261 PG_RETURN_DATUM(value);
265 * domain_recv - binary input routine for any domain type.
267 Datum
268 domain_recv(PG_FUNCTION_ARGS)
270 StringInfo buf;
271 Oid domainType;
272 DomainIOData *my_extra;
273 Datum value;
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.
280 if (PG_ARGISNULL(0))
281 buf = NULL;
282 else
283 buf = (StringInfo) PG_GETARG_POINTER(0);
284 if (PG_ARGISNULL(1))
285 PG_RETURN_NULL();
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,
305 buf,
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);
314 if (buf == NULL)
315 PG_RETURN_NULL();
316 else
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.
326 void
327 domain_check(Datum value, bool isnull, Oid domainType,
328 void **extra, MemoryContext mcxt)
330 DomainIOData *my_extra = NULL;
332 if (mcxt == 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).
340 if (extra)
341 my_extra = (DomainIOData *) *extra;
342 if (my_extra == NULL || my_extra->domain_type != domainType)
344 my_extra = domain_state_setup(domainType, true, mcxt);
345 if (extra)
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)
362 HeapTuple tup;
363 Form_pg_type typtup;
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 */