PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2017, 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 */
61 } DomainIOData;
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.
86  */
87  typentry = lookup_type_cache(domainType, 0);
88  if (typentry->typtype != TYPTYPE_DOMAIN)
89  ereport(ERROR,
90  (errcode(ERRCODE_DATATYPE_MISMATCH),
91  errmsg("type %s is not a domain",
92  format_type_be(domainType))));
93 
94  /* Find out the base type */
95  my_extra->typtypmod = -1;
96  baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
97 
98  /* Look up underlying I/O function */
99  if (binary)
100  getTypeBinaryInputInfo(baseType,
101  &my_extra->typiofunc,
102  &my_extra->typioparam);
103  else
104  getTypeInputInfo(baseType,
105  &my_extra->typiofunc,
106  &my_extra->typioparam);
107  fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
108 
109  /* Look up constraints for domain */
110  InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt);
111 
112  /* We don't make an ExprContext until needed */
113  my_extra->econtext = NULL;
114  my_extra->mcxt = mcxt;
115 
116  /* Mark cache valid */
117  my_extra->domain_type = domainType;
118 
119  return my_extra;
120 }
121 
122 /*
123  * domain_check_input - apply the cached checks.
124  *
125  * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
126  */
127 static void
128 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
129 {
130  ExprContext *econtext = my_extra->econtext;
131  ListCell *l;
132 
133  /* Make sure we have up-to-date constraints */
135 
136  foreach(l, my_extra->constraint_ref.constraints)
137  {
139 
140  switch (con->constrainttype)
141  {
143  if (isnull)
144  ereport(ERROR,
145  (errcode(ERRCODE_NOT_NULL_VIOLATION),
146  errmsg("domain %s does not allow null values",
147  format_type_be(my_extra->domain_type)),
148  errdatatype(my_extra->domain_type)));
149  break;
151  {
152  Datum conResult;
153  bool conIsNull;
154 
155  /* Make the econtext if we didn't already */
156  if (econtext == NULL)
157  {
158  MemoryContext oldcontext;
159 
160  oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
161  econtext = CreateStandaloneExprContext();
162  MemoryContextSwitchTo(oldcontext);
163  my_extra->econtext = econtext;
164  }
165 
166  /*
167  * Set up value to be returned by CoerceToDomainValue
168  * nodes. Unlike ExecEvalCoerceToDomain, this econtext
169  * couldn't be shared with anything else, so no need to
170  * save and restore fields. But we do need to protect the
171  * passed-in value against being changed by called
172  * functions. (It couldn't be a R/W expanded object for
173  * most uses, but that seems possible for domain_check().)
174  */
175  econtext->domainValue_datum =
176  MakeExpandedObjectReadOnly(value, isnull,
177  my_extra->constraint_ref.tcache->typlen);
178  econtext->domainValue_isNull = isnull;
179 
180  conResult = ExecEvalExprSwitchContext(con->check_expr,
181  econtext,
182  &conIsNull);
183 
184  if (!conIsNull &&
185  !DatumGetBool(conResult))
186  ereport(ERROR,
187  (errcode(ERRCODE_CHECK_VIOLATION),
188  errmsg("value for domain %s violates check constraint \"%s\"",
189  format_type_be(my_extra->domain_type),
190  con->name),
192  con->name)));
193  break;
194  }
195  default:
196  elog(ERROR, "unrecognized constraint type: %d",
197  (int) con->constrainttype);
198  break;
199  }
200  }
201 
202  /*
203  * Before exiting, call any shutdown callbacks and reset econtext's
204  * per-tuple memory. This avoids leaking non-memory resources, if
205  * anything in the expression(s) has any.
206  */
207  if (econtext)
208  ReScanExprContext(econtext);
209 }
210 
211 
212 /*
213  * domain_in - input routine for any domain type.
214  */
215 Datum
217 {
218  char *string;
219  Oid domainType;
220  DomainIOData *my_extra;
221  Datum value;
222 
223  /*
224  * Since domain_in is not strict, we have to check for null inputs. The
225  * typioparam argument should never be null in normal system usage, but it
226  * could be null in a manual invocation --- if so, just return null.
227  */
228  if (PG_ARGISNULL(0))
229  string = NULL;
230  else
231  string = PG_GETARG_CSTRING(0);
232  if (PG_ARGISNULL(1))
233  PG_RETURN_NULL();
234  domainType = PG_GETARG_OID(1);
235 
236  /*
237  * We arrange to look up the needed info just once per series of calls,
238  * assuming the domain type doesn't change underneath us (which really
239  * shouldn't happen, but cope if it does).
240  */
241  my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
242  if (my_extra == NULL || my_extra->domain_type != domainType)
243  {
244  my_extra = domain_state_setup(domainType, false,
245  fcinfo->flinfo->fn_mcxt);
246  fcinfo->flinfo->fn_extra = (void *) my_extra;
247  }
248 
249  /*
250  * Invoke the base type's typinput procedure to convert the data.
251  */
252  value = InputFunctionCall(&my_extra->proc,
253  string,
254  my_extra->typioparam,
255  my_extra->typtypmod);
256 
257  /*
258  * Do the necessary checks to ensure it's a valid domain value.
259  */
260  domain_check_input(value, (string == NULL), my_extra);
261 
262  if (string == NULL)
263  PG_RETURN_NULL();
264  else
265  PG_RETURN_DATUM(value);
266 }
267 
268 /*
269  * domain_recv - binary input routine for any domain type.
270  */
271 Datum
273 {
274  StringInfo buf;
275  Oid domainType;
276  DomainIOData *my_extra;
277  Datum value;
278 
279  /*
280  * Since domain_recv is not strict, we have to check for null inputs. The
281  * typioparam argument should never be null in normal system usage, but it
282  * could be null in a manual invocation --- if so, just return null.
283  */
284  if (PG_ARGISNULL(0))
285  buf = NULL;
286  else
287  buf = (StringInfo) PG_GETARG_POINTER(0);
288  if (PG_ARGISNULL(1))
289  PG_RETURN_NULL();
290  domainType = PG_GETARG_OID(1);
291 
292  /*
293  * We arrange to look up the needed info just once per series of calls,
294  * assuming the domain type doesn't change underneath us (which really
295  * shouldn't happen, but cope if it does).
296  */
297  my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
298  if (my_extra == NULL || my_extra->domain_type != domainType)
299  {
300  my_extra = domain_state_setup(domainType, true,
301  fcinfo->flinfo->fn_mcxt);
302  fcinfo->flinfo->fn_extra = (void *) my_extra;
303  }
304 
305  /*
306  * Invoke the base type's typreceive procedure to convert the data.
307  */
308  value = ReceiveFunctionCall(&my_extra->proc,
309  buf,
310  my_extra->typioparam,
311  my_extra->typtypmod);
312 
313  /*
314  * Do the necessary checks to ensure it's a valid domain value.
315  */
316  domain_check_input(value, (buf == NULL), my_extra);
317 
318  if (buf == NULL)
319  PG_RETURN_NULL();
320  else
321  PG_RETURN_DATUM(value);
322 }
323 
324 /*
325  * domain_check - check that a datum satisfies the constraints of a
326  * domain. extra and mcxt can be passed if they are available from,
327  * say, a FmgrInfo structure, or they can be NULL, in which case the
328  * setup is repeated for each call.
329  */
330 void
331 domain_check(Datum value, bool isnull, Oid domainType,
332  void **extra, MemoryContext mcxt)
333 {
334  DomainIOData *my_extra = NULL;
335 
336  if (mcxt == NULL)
337  mcxt = CurrentMemoryContext;
338 
339  /*
340  * We arrange to look up the needed info just once per series of calls,
341  * assuming the domain type doesn't change underneath us (which really
342  * shouldn't happen, but cope if it does).
343  */
344  if (extra)
345  my_extra = (DomainIOData *) *extra;
346  if (my_extra == NULL || my_extra->domain_type != domainType)
347  {
348  my_extra = domain_state_setup(domainType, true, mcxt);
349  if (extra)
350  *extra = (void *) my_extra;
351  }
352 
353  /*
354  * Do the necessary checks to ensure it's a valid domain value.
355  */
356  domain_check_input(value, isnull, my_extra);
357 }
358 
359 /*
360  * errdatatype --- stores schema_name and datatype_name of a datatype
361  * within the current errordata.
362  */
363 int
364 errdatatype(Oid datatypeOid)
365 {
366  HeapTuple tup;
367  Form_pg_type typtup;
368 
369  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(datatypeOid));
370  if (!HeapTupleIsValid(tup))
371  elog(ERROR, "cache lookup failed for type %u", datatypeOid);
372  typtup = (Form_pg_type) GETSTRUCT(tup);
373 
375  get_namespace_name(typtup->typnamespace));
377 
378  ReleaseSysCache(tup);
379 
380  return 0; /* return value does not matter */
381 }
382 
383 /*
384  * errdomainconstraint --- stores schema_name, datatype_name and
385  * constraint_name of a domain-related constraint within the current errordata.
386  */
387 int
388 errdomainconstraint(Oid datatypeOid, const char *conname)
389 {
390  errdatatype(datatypeOid);
392 
393  return 0; /* return value does not matter */
394 }
#define TYPTYPE_DOMAIN
Definition: pg_type.h:718
ExprContext * CreateStandaloneExprContext(void)
Definition: execUtils.c:281
Definition: fmgr.h:53
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2256
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
static struct @78 value
#define PG_DIAG_SCHEMA_NAME
Definition: postgres_ext.h:65
void UpdateDomainConstraintRef(DomainConstraintRef *ref)
Definition: typcache.c:1001
DomainConstraintType constrainttype
Definition: execnodes.h:1024
void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, MemoryContext refctx)
Definition: typcache.c:967
StringInfoData * StringInfo
Definition: stringinfo.h:46
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
ExprState * check_expr
Definition: execnodes.h:1026
int errcode(int sqlerrcode)
Definition: elog.c:575
Datum domain_in(PG_FUNCTION_ARGS)
Definition: domains.c:216
Oid domain_type
Definition: domains.c:49
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:233
FormData_pg_type * Form_pg_type
Definition: pg_type.h:233
unsigned int Oid
Definition: postgres_ext.h:31
Datum domainValue_datum
Definition: execnodes.h:154
int16 typlen
Definition: typcache.h:35
FmgrInfo proc
Definition: domains.c:54
Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext, bool *isNull)
Definition: execQual.c:4220
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:152
signed int int32
Definition: c.h:256
int32 typtypmod
Definition: domains.c:53
struct DomainIOData DomainIOData
Datum domain_recv(PG_FUNCTION_ARGS)
Definition: domains.c:272
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
int errdomainconstraint(Oid datatypeOid, const char *conname)
Definition: domains.c:388
Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod)
Definition: fmgr.c:1990
ExprContext * econtext
Definition: domains.c:58
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3006
static char * buf
Definition: pg_test_fsync.c:65
#define PG_GETARG_OID(n)
Definition: fmgr.h:232
char string[11]
Definition: preproc-type.c:46
#define DatumGetBool(X)
Definition: postgres.h:399
#define MakeExpandedObjectReadOnly(d, isnull, typlen)
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:169
void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam)
Definition: lsyscache.c:2633
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2567
#define ereport(elevel, rest)
Definition: elog.h:122
bool domainValue_isNull
Definition: execnodes.h:155
Oid typiofunc
Definition: domains.c:51
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1116
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:305
int errdatatype(Oid datatypeOid)
Definition: domains.c:364
#define PG_DIAG_DATATYPE_NAME
Definition: postgres_ext.h:68
void domain_check(Datum value, bool isnull, Oid domainType, void **extra, MemoryContext mcxt)
Definition: domains.c:331
#define PG_DIAG_CONSTRAINT_NAME
Definition: postgres_ext.h:69
Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1932
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:191
DomainConstraintRef constraint_ref
Definition: domains.c:56
char typtype
Definition: typcache.h:39
Oid typioparam
Definition: domains.c:52
#define PG_ARGISNULL(n)
Definition: fmgr.h:166
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
TypeCacheEntry * tcache
Definition: typcache.h:134
void ReScanExprContext(ExprContext *econtext)
Definition: execUtils.c:367
static DomainIOData * domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
Definition: domains.c:73
MemoryContext mcxt
Definition: domains.c:60
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:707
#define NameStr(name)
Definition: c.h:499
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:234
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
#define elog
Definition: elog.h:219
int err_generic_string(int field, const char *str)
Definition: elog.c:1191
#define PG_RETURN_NULL()
Definition: fmgr.h:297
static void domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
Definition: domains.c:128