PostgreSQL Source Code  git master
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-2024, 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 
43 static 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  */
50 typedef 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  */
75 static DomainIOData *
76 domain_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)
93  ereport(ERROR,
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  */
137 static void
138 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra,
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  */
217 fail:
218  if (econtext)
219  ReScanExprContext(econtext);
220 }
221 
222 
223 /*
224  * domain_in - input routine for any domain type.
225  */
226 Datum
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))
245  PG_RETURN_NULL();
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))
270  PG_RETURN_NULL();
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)
278  PG_RETURN_NULL();
279  else
281 }
282 
283 /*
284  * domain_recv - binary input routine for any domain type.
285  */
286 Datum
288 {
289  StringInfo buf;
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))
304  PG_RETURN_NULL();
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)
334  PG_RETURN_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  */
345 void
346 domain_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(). */
354 bool
355 domain_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  */
370 static bool
371 domain_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)
378  mcxt = CurrentMemoryContext;
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  */
406 int
407 errdatatype(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  */
430 int
431 errdomainconstraint(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:725
signed int int32
Definition: c.h:482
static bool domain_check_internal(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt, Node *escontext)
Definition: domains.c:371
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
static DomainIOData * domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
Definition: domains.c:76
int err_generic_string(int field, const char *str)
Definition: elog.c:1512
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define errsave(context,...)
Definition: elog.h:261
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:850
void ReScanExprContext(ExprContext *econtext)
Definition: execUtils.c:443
ExprContext * CreateStandaloneExprContext(void)
Definition: execUtils.c:357
@ DOM_CONSTRAINT_CHECK
Definition: execnodes.h:1012
@ DOM_CONSTRAINT_NOTNULL
Definition: execnodes.h:1011
#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
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
static struct @160 value
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3366
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2874
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition: lsyscache.c:2940
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
#define SOFT_ERROR_OCCURRED(escontext)
Definition: miscnodes.h:53
#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:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define PG_DIAG_SCHEMA_NAME
Definition: postgres_ext.h:64
#define PG_DIAG_CONSTRAINT_NAME
Definition: postgres_ext.h:68
#define PG_DIAG_DATATYPE_NAME
Definition: postgres_ext.h:67
unsigned int Oid
Definition: postgres_ext.h:31
char string[11]
Definition: preproc-type.c:52
MemoryContextSwitchTo(old_ctx)
StringInfoData * StringInfo
Definition: stringinfo.h:54
TypeCacheEntry * tcache
Definition: typcache.h:168
DomainConstraintType constrainttype
Definition: execnodes.h:1018
ExprState * check_exprstate
Definition: execnodes.h:1021
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:289
bool domainValue_isNull
Definition: execnodes.h:291
Definition: fmgr.h:57
Definition: nodes.h:129
int32 domainBaseTypmod
Definition: typcache.h:115
char typtype
Definition: typcache.h:43
int16 typlen
Definition: typcache.h:39
Oid domainBaseType
Definition: typcache.h:114
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:1400
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:386
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition: typcache.c:1438
#define TYPECACHE_DOMAIN_BASE_INFO
Definition: typcache.h:149