Cache NO ACTION foreign keys separately from RESTRICT foreign keys
[pgsql.git] / src / backend / utils / adt / domains.c
blobe755b70e17ec90f7f0c8f11dee7dc3d1460fe31f
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-2025, 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"
43 static bool domain_check_internal(Datum value, bool isnull, Oid domainType,
44 void **extra, MemoryContext mcxt,
45 Node *escontext);
48 * structure to cache state across multiple calls
50 typedef struct DomainIOData
52 Oid domain_type;
53 /* Data needed to call base type's input function */
54 Oid typiofunc;
55 Oid typioparam;
56 int32 typtypmod;
57 FmgrInfo proc;
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 */
63 MemoryContext mcxt;
64 } DomainIOData;
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.
75 static DomainIOData *
76 domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
78 DomainIOData *my_extra;
79 TypeCacheEntry *typentry;
80 Oid baseType;
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)
93 ereport(ERROR,
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 */
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);
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;
123 return my_extra;
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.
137 static void
138 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra,
139 Node *escontext)
141 ExprContext *econtext = my_extra->econtext;
142 ListCell *l;
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:
154 if (isnull)
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;
163 break;
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
185 * domain_check().)
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))
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),
199 errdomainconstraint(my_extra->domain_type,
200 con->name)));
201 goto fail;
203 break;
205 default:
206 elog(ERROR, "unrecognized constraint type: %d",
207 (int) con->constrainttype);
208 break;
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.
217 fail:
218 if (econtext)
219 ReScanExprContext(econtext);
224 * domain_in - input routine for any domain type.
226 Datum
227 domain_in(PG_FUNCTION_ARGS)
229 char *string;
230 Oid domainType;
231 Node *escontext = fcinfo->context;
232 DomainIOData *my_extra;
233 Datum value;
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.
240 if (PG_ARGISNULL(0))
241 string = NULL;
242 else
243 string = PG_GETARG_CSTRING(0);
244 if (PG_ARGISNULL(1))
245 PG_RETURN_NULL();
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,
265 string,
266 my_extra->typioparam,
267 my_extra->typtypmod,
268 escontext,
269 &value))
270 PG_RETURN_NULL();
273 * Do the necessary checks to ensure it's a valid domain value.
275 domain_check_input(value, (string == NULL), my_extra, escontext);
277 if (string == NULL)
278 PG_RETURN_NULL();
279 else
280 PG_RETURN_DATUM(value);
284 * domain_recv - binary input routine for any domain type.
286 Datum
287 domain_recv(PG_FUNCTION_ARGS)
289 StringInfo buf;
290 Oid domainType;
291 DomainIOData *my_extra;
292 Datum value;
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.
299 if (PG_ARGISNULL(0))
300 buf = NULL;
301 else
302 buf = (StringInfo) PG_GETARG_POINTER(0);
303 if (PG_ARGISNULL(1))
304 PG_RETURN_NULL();
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,
324 buf,
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);
333 if (buf == NULL)
334 PG_RETURN_NULL();
335 else
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.
345 void
346 domain_check(Datum value, bool isnull, Oid domainType,
347 void **extra, MemoryContext mcxt)
349 (void) domain_check_internal(value, isnull, domainType, extra, mcxt,
350 NULL);
353 /* Error-safe variant of domain_check(). */
354 bool
355 domain_check_safe(Datum value, bool isnull, Oid domainType,
356 void **extra, MemoryContext mcxt,
357 Node *escontext)
359 return domain_check_internal(value, isnull, domainType, extra, mcxt,
360 escontext);
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.
370 static bool
371 domain_check_internal(Datum value, bool isnull, Oid domainType,
372 void **extra, MemoryContext mcxt,
373 Node *escontext)
375 DomainIOData *my_extra = NULL;
377 if (mcxt == 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).
385 if (extra)
386 my_extra = (DomainIOData *) *extra;
387 if (my_extra == NULL || my_extra->domain_type != domainType)
389 my_extra = domain_state_setup(domainType, true, mcxt);
390 if (extra)
391 *extra = my_extra;
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)
409 HeapTuple tup;
410 Form_pg_type typtup;
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 */