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-2023, 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 
44 /*
45  * structure to cache state across multiple calls
46  */
47 typedef struct DomainIOData
48 {
50  /* Data needed to call base type's input function */
55  /* Reference to cached list of constraint items to check */
57  /* Context for evaluating CHECK constraints in */
59  /* Memory context this cache is in */
62 
63 
64 /*
65  * domain_state_setup - initialize the cache for a new domain type.
66  *
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.
71  */
72 static DomainIOData *
73 domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
74 {
75  DomainIOData *my_extra;
76  TypeCacheEntry *typentry;
77  Oid baseType;
78 
79  my_extra = (DomainIOData *) MemoryContextAlloc(mcxt, sizeof(DomainIOData));
80 
81  /*
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.
87  */
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))));
94 
95  /* Find out the base type */
96  baseType = typentry->domainBaseType;
97  my_extra->typtypmod = typentry->domainBaseTypmod;
98 
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);
109 
110  /* Look up constraints for domain */
111  InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt, true);
112 
113  /* We don't make an ExprContext until needed */
114  my_extra->econtext = NULL;
115  my_extra->mcxt = mcxt;
116 
117  /* Mark cache valid */
118  my_extra->domain_type = domainType;
119 
120  return my_extra;
121 }
122 
123 /*
124  * domain_check_input - apply the cached checks.
125  *
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.
129  *
130  * If escontext points to an ErrorSaveContext, any failures are reported
131  * there, otherwise they are ereport'ed. Note that we do not attempt to do
132  * soft reporting of errors raised during execution of CHECK constraints.
133  */
134 static void
135 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra,
136  Node *escontext)
137 {
138  ExprContext *econtext = my_extra->econtext;
139  ListCell *l;
140 
141  /* Make sure we have up-to-date constraints */
143 
144  foreach(l, my_extra->constraint_ref.constraints)
145  {
147 
148  switch (con->constrainttype)
149  {
151  if (isnull)
152  {
153  errsave(escontext,
154  (errcode(ERRCODE_NOT_NULL_VIOLATION),
155  errmsg("domain %s does not allow null values",
156  format_type_be(my_extra->domain_type)),
157  errdatatype(my_extra->domain_type)));
158  goto fail;
159  }
160  break;
162  {
163  /* Make the econtext if we didn't already */
164  if (econtext == NULL)
165  {
166  MemoryContext oldcontext;
167 
168  oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
169  econtext = CreateStandaloneExprContext();
170  MemoryContextSwitchTo(oldcontext);
171  my_extra->econtext = econtext;
172  }
173 
174  /*
175  * Set up value to be returned by CoerceToDomainValue
176  * nodes. Unlike in the generic expression case, this
177  * econtext couldn't be shared with anything else, so no
178  * need to save and restore fields. But we do need to
179  * protect the passed-in value against being changed by
180  * called functions. (It couldn't be a R/W expanded
181  * object for most uses, but that seems possible for
182  * domain_check().)
183  */
184  econtext->domainValue_datum =
186  my_extra->constraint_ref.tcache->typlen);
187  econtext->domainValue_isNull = isnull;
188 
189  if (!ExecCheck(con->check_exprstate, econtext))
190  {
191  errsave(escontext,
192  (errcode(ERRCODE_CHECK_VIOLATION),
193  errmsg("value for domain %s violates check constraint \"%s\"",
194  format_type_be(my_extra->domain_type),
195  con->name),
197  con->name)));
198  goto fail;
199  }
200  break;
201  }
202  default:
203  elog(ERROR, "unrecognized constraint type: %d",
204  (int) con->constrainttype);
205  break;
206  }
207  }
208 
209  /*
210  * Before exiting, call any shutdown callbacks and reset econtext's
211  * per-tuple memory. This avoids leaking non-memory resources, if
212  * anything in the expression(s) has any.
213  */
214 fail:
215  if (econtext)
216  ReScanExprContext(econtext);
217 }
218 
219 
220 /*
221  * domain_in - input routine for any domain type.
222  */
223 Datum
225 {
226  char *string;
227  Oid domainType;
228  Node *escontext = fcinfo->context;
229  DomainIOData *my_extra;
230  Datum value;
231 
232  /*
233  * Since domain_in is not strict, we have to check for null inputs. The
234  * typioparam argument should never be null in normal system usage, but it
235  * could be null in a manual invocation --- if so, just return null.
236  */
237  if (PG_ARGISNULL(0))
238  string = NULL;
239  else
240  string = PG_GETARG_CSTRING(0);
241  if (PG_ARGISNULL(1))
242  PG_RETURN_NULL();
243  domainType = PG_GETARG_OID(1);
244 
245  /*
246  * We arrange to look up the needed info just once per series of calls,
247  * assuming the domain type doesn't change underneath us (which really
248  * shouldn't happen, but cope if it does).
249  */
250  my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
251  if (my_extra == NULL || my_extra->domain_type != domainType)
252  {
253  my_extra = domain_state_setup(domainType, false,
254  fcinfo->flinfo->fn_mcxt);
255  fcinfo->flinfo->fn_extra = (void *) my_extra;
256  }
257 
258  /*
259  * Invoke the base type's typinput procedure to convert the data.
260  */
261  if (!InputFunctionCallSafe(&my_extra->proc,
262  string,
263  my_extra->typioparam,
264  my_extra->typtypmod,
265  escontext,
266  &value))
267  PG_RETURN_NULL();
268 
269  /*
270  * Do the necessary checks to ensure it's a valid domain value.
271  */
272  domain_check_input(value, (string == NULL), my_extra, escontext);
273 
274  if (string == NULL)
275  PG_RETURN_NULL();
276  else
278 }
279 
280 /*
281  * domain_recv - binary input routine for any domain type.
282  */
283 Datum
285 {
286  StringInfo buf;
287  Oid domainType;
288  DomainIOData *my_extra;
289  Datum value;
290 
291  /*
292  * Since domain_recv is not strict, we have to check for null inputs. The
293  * typioparam argument should never be null in normal system usage, but it
294  * could be null in a manual invocation --- if so, just return null.
295  */
296  if (PG_ARGISNULL(0))
297  buf = NULL;
298  else
300  if (PG_ARGISNULL(1))
301  PG_RETURN_NULL();
302  domainType = PG_GETARG_OID(1);
303 
304  /*
305  * We arrange to look up the needed info just once per series of calls,
306  * assuming the domain type doesn't change underneath us (which really
307  * shouldn't happen, but cope if it does).
308  */
309  my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
310  if (my_extra == NULL || my_extra->domain_type != domainType)
311  {
312  my_extra = domain_state_setup(domainType, true,
313  fcinfo->flinfo->fn_mcxt);
314  fcinfo->flinfo->fn_extra = (void *) my_extra;
315  }
316 
317  /*
318  * Invoke the base type's typreceive procedure to convert the data.
319  */
320  value = ReceiveFunctionCall(&my_extra->proc,
321  buf,
322  my_extra->typioparam,
323  my_extra->typtypmod);
324 
325  /*
326  * Do the necessary checks to ensure it's a valid domain value.
327  */
328  domain_check_input(value, (buf == NULL), my_extra, NULL);
329 
330  if (buf == NULL)
331  PG_RETURN_NULL();
332  else
334 }
335 
336 /*
337  * domain_check - check that a datum satisfies the constraints of a
338  * domain. extra and mcxt can be passed if they are available from,
339  * say, a FmgrInfo structure, or they can be NULL, in which case the
340  * setup is repeated for each call.
341  */
342 void
343 domain_check(Datum value, bool isnull, Oid domainType,
344  void **extra, MemoryContext mcxt)
345 {
346  DomainIOData *my_extra = NULL;
347 
348  if (mcxt == NULL)
349  mcxt = CurrentMemoryContext;
350 
351  /*
352  * We arrange to look up the needed info just once per series of calls,
353  * assuming the domain type doesn't change underneath us (which really
354  * shouldn't happen, but cope if it does).
355  */
356  if (extra)
357  my_extra = (DomainIOData *) *extra;
358  if (my_extra == NULL || my_extra->domain_type != domainType)
359  {
360  my_extra = domain_state_setup(domainType, true, mcxt);
361  if (extra)
362  *extra = (void *) my_extra;
363  }
364 
365  /*
366  * Do the necessary checks to ensure it's a valid domain value.
367  */
368  domain_check_input(value, isnull, my_extra, NULL);
369 }
370 
371 /*
372  * errdatatype --- stores schema_name and datatype_name of a datatype
373  * within the current errordata.
374  */
375 int
376 errdatatype(Oid datatypeOid)
377 {
378  HeapTuple tup;
379  Form_pg_type typtup;
380 
381  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(datatypeOid));
382  if (!HeapTupleIsValid(tup))
383  elog(ERROR, "cache lookup failed for type %u", datatypeOid);
384  typtup = (Form_pg_type) GETSTRUCT(tup);
385 
387  get_namespace_name(typtup->typnamespace));
389 
390  ReleaseSysCache(tup);
391 
392  return 0; /* return value does not matter */
393 }
394 
395 /*
396  * errdomainconstraint --- stores schema_name, datatype_name and
397  * constraint_name of a domain-related constraint within the current errordata.
398  */
399 int
400 errdomainconstraint(Oid datatypeOid, const char *conname)
401 {
402  errdatatype(datatypeOid);
404 
405  return 0; /* return value does not matter */
406 }
#define NameStr(name)
Definition: c.h:735
signed int int32
Definition: c.h:483
struct DomainIOData DomainIOData
int errdatatype(Oid datatypeOid)
Definition: domains.c:376
Datum domain_recv(PG_FUNCTION_ARGS)
Definition: domains.c:284
int errdomainconstraint(Oid datatypeOid, const char *conname)
Definition: domains.c:400
Datum domain_in(PG_FUNCTION_ARGS)
Definition: domains.c:224
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
Definition: domains.c:343
static void domain_check_input(Datum value, bool isnull, DomainIOData *my_extra, Node *escontext)
Definition: domains.c:135
static DomainIOData * domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
Definition: domains.c:73
int err_generic_string(int field, const char *str)
Definition: elog.c:1511
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define errsave(context,...)
Definition: elog.h:260
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:843
void ReScanExprContext(ExprContext *econtext)
Definition: execUtils.c:446
ExprContext * CreateStandaloneExprContext(void)
Definition: execUtils.c:360
@ DOM_CONSTRAINT_CHECK
Definition: execnodes.h:993
@ DOM_CONSTRAINT_NOTNULL
Definition: execnodes.h:992
#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:1568
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1680
#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 @148 value
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3348
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2856
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition: lsyscache.c:2922
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1021
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
#define lfirst(lc)
Definition: pg_list.h:172
static char * buf
Definition: pg_test_fsync.c:67
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
StringInfoData * StringInfo
Definition: stringinfo.h:44
TypeCacheEntry * tcache
Definition: typcache.h:167
DomainConstraintType constrainttype
Definition: execnodes.h:999
ExprState * check_exprstate
Definition: execnodes.h:1002
int32 typtypmod
Definition: domains.c:53
Oid domain_type
Definition: domains.c:49
DomainConstraintRef constraint_ref
Definition: domains.c:56
FmgrInfo proc
Definition: domains.c:54
ExprContext * econtext
Definition: domains.c:58
MemoryContext mcxt
Definition: domains.c:60
Oid typiofunc
Definition: domains.c:51
Oid typioparam
Definition: domains.c:52
Datum domainValue_datum
Definition: execnodes.h:280
bool domainValue_isNull
Definition: execnodes.h:282
Definition: fmgr.h:57
Definition: nodes.h:129
int32 domainBaseTypmod
Definition: typcache.h:114
char typtype
Definition: typcache.h:43
int16 typlen
Definition: typcache.h:39
Oid domainBaseType
Definition: typcache.h:113
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:868
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:820
@ TYPEOID
Definition: syscache.h:114
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx, bool need_exprstate)
Definition: typcache.c:1310
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:344
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition: typcache.c:1348
#define TYPECACHE_DOMAIN_BASE_INFO
Definition: typcache.h:148