Line data Source code
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 : {
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;
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 361 : domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
74 : {
75 : DomainIOData *my_extra;
76 : TypeCacheEntry *typentry;
77 : Oid baseType;
78 :
79 361 : 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 361 : typentry = lookup_type_cache(domainType, 0);
88 361 : if (typentry->typtype != TYPTYPE_DOMAIN)
89 0 : 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 361 : my_extra->typtypmod = -1;
96 361 : baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
97 :
98 : /* Look up underlying I/O function */
99 361 : if (binary)
100 312 : getTypeBinaryInputInfo(baseType,
101 : &my_extra->typiofunc,
102 : &my_extra->typioparam);
103 : else
104 49 : getTypeInputInfo(baseType,
105 : &my_extra->typiofunc,
106 : &my_extra->typioparam);
107 361 : fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
108 :
109 : /* Look up constraints for domain */
110 361 : InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt, true);
111 :
112 : /* We don't make an ExprContext until needed */
113 361 : my_extra->econtext = NULL;
114 361 : my_extra->mcxt = mcxt;
115 :
116 : /* Mark cache valid */
117 361 : my_extra->domain_type = domainType;
118 :
119 361 : return my_extra;
120 : }
121 :
122 : /*
123 : * domain_check_input - apply the cached checks.
124 : *
125 : * This is roughly similar to the handling of CoerceToDomain nodes in
126 : * execExpr*.c, but we execute each constraint separately, rather than
127 : * compiling them in-line within a larger expression.
128 : */
129 : static void
130 4529 : domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
131 : {
132 4529 : ExprContext *econtext = my_extra->econtext;
133 : ListCell *l;
134 :
135 : /* Make sure we have up-to-date constraints */
136 4529 : UpdateDomainConstraintRef(&my_extra->constraint_ref);
137 :
138 5667 : foreach(l, my_extra->constraint_ref.constraints)
139 : {
140 1148 : DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
141 :
142 1148 : switch (con->constrainttype)
143 : {
144 : case DOM_CONSTRAINT_NOTNULL:
145 22 : if (isnull)
146 6 : ereport(ERROR,
147 : (errcode(ERRCODE_NOT_NULL_VIOLATION),
148 : errmsg("domain %s does not allow null values",
149 : format_type_be(my_extra->domain_type)),
150 : errdatatype(my_extra->domain_type)));
151 16 : break;
152 : case DOM_CONSTRAINT_CHECK:
153 : {
154 : /* Make the econtext if we didn't already */
155 1126 : if (econtext == NULL)
156 : {
157 : MemoryContext oldcontext;
158 :
159 318 : oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
160 318 : econtext = CreateStandaloneExprContext();
161 318 : MemoryContextSwitchTo(oldcontext);
162 318 : my_extra->econtext = econtext;
163 : }
164 :
165 : /*
166 : * Set up value to be returned by CoerceToDomainValue
167 : * nodes. Unlike in the generic expression case, this
168 : * econtext couldn't be shared with anything else, so no
169 : * need to save and restore fields. But we do need to
170 : * protect the passed-in value against being changed by
171 : * called functions. (It couldn't be a R/W expanded
172 : * object for most uses, but that seems possible for
173 : * domain_check().)
174 : */
175 1126 : econtext->domainValue_datum =
176 1126 : MakeExpandedObjectReadOnly(value, isnull,
177 : my_extra->constraint_ref.tcache->typlen);
178 1126 : econtext->domainValue_isNull = isnull;
179 :
180 1126 : if (!ExecCheck(con->check_exprstate, econtext))
181 4 : ereport(ERROR,
182 : (errcode(ERRCODE_CHECK_VIOLATION),
183 : errmsg("value for domain %s violates check constraint \"%s\"",
184 : format_type_be(my_extra->domain_type),
185 : con->name),
186 : errdomainconstraint(my_extra->domain_type,
187 : con->name)));
188 1122 : break;
189 : }
190 : default:
191 0 : elog(ERROR, "unrecognized constraint type: %d",
192 : (int) con->constrainttype);
193 : break;
194 : }
195 : }
196 :
197 : /*
198 : * Before exiting, call any shutdown callbacks and reset econtext's
199 : * per-tuple memory. This avoids leaking non-memory resources, if
200 : * anything in the expression(s) has any.
201 : */
202 4519 : if (econtext)
203 1122 : ReScanExprContext(econtext);
204 4519 : }
205 :
206 :
207 : /*
208 : * domain_in - input routine for any domain type.
209 : */
210 : Datum
211 4083 : domain_in(PG_FUNCTION_ARGS)
212 : {
213 : char *string;
214 : Oid domainType;
215 : DomainIOData *my_extra;
216 : Datum value;
217 :
218 : /*
219 : * Since domain_in is not strict, we have to check for null inputs. The
220 : * typioparam argument should never be null in normal system usage, but it
221 : * could be null in a manual invocation --- if so, just return null.
222 : */
223 4083 : if (PG_ARGISNULL(0))
224 22 : string = NULL;
225 : else
226 4061 : string = PG_GETARG_CSTRING(0);
227 4083 : if (PG_ARGISNULL(1))
228 0 : PG_RETURN_NULL();
229 4083 : domainType = PG_GETARG_OID(1);
230 :
231 : /*
232 : * We arrange to look up the needed info just once per series of calls,
233 : * assuming the domain type doesn't change underneath us (which really
234 : * shouldn't happen, but cope if it does).
235 : */
236 4083 : my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
237 4083 : if (my_extra == NULL || my_extra->domain_type != domainType)
238 : {
239 49 : my_extra = domain_state_setup(domainType, false,
240 49 : fcinfo->flinfo->fn_mcxt);
241 49 : fcinfo->flinfo->fn_extra = (void *) my_extra;
242 : }
243 :
244 : /*
245 : * Invoke the base type's typinput procedure to convert the data.
246 : */
247 4083 : value = InputFunctionCall(&my_extra->proc,
248 : string,
249 : my_extra->typioparam,
250 : my_extra->typtypmod);
251 :
252 : /*
253 : * Do the necessary checks to ensure it's a valid domain value.
254 : */
255 4081 : domain_check_input(value, (string == NULL), my_extra);
256 :
257 4079 : if (string == NULL)
258 20 : PG_RETURN_NULL();
259 : else
260 4059 : PG_RETURN_DATUM(value);
261 : }
262 :
263 : /*
264 : * domain_recv - binary input routine for any domain type.
265 : */
266 : Datum
267 0 : domain_recv(PG_FUNCTION_ARGS)
268 : {
269 : StringInfo buf;
270 : Oid domainType;
271 : DomainIOData *my_extra;
272 : Datum value;
273 :
274 : /*
275 : * Since domain_recv is not strict, we have to check for null inputs. The
276 : * typioparam argument should never be null in normal system usage, but it
277 : * could be null in a manual invocation --- if so, just return null.
278 : */
279 0 : if (PG_ARGISNULL(0))
280 0 : buf = NULL;
281 : else
282 0 : buf = (StringInfo) PG_GETARG_POINTER(0);
283 0 : if (PG_ARGISNULL(1))
284 0 : PG_RETURN_NULL();
285 0 : domainType = PG_GETARG_OID(1);
286 :
287 : /*
288 : * We arrange to look up the needed info just once per series of calls,
289 : * assuming the domain type doesn't change underneath us (which really
290 : * shouldn't happen, but cope if it does).
291 : */
292 0 : my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
293 0 : if (my_extra == NULL || my_extra->domain_type != domainType)
294 : {
295 0 : my_extra = domain_state_setup(domainType, true,
296 0 : fcinfo->flinfo->fn_mcxt);
297 0 : fcinfo->flinfo->fn_extra = (void *) my_extra;
298 : }
299 :
300 : /*
301 : * Invoke the base type's typreceive procedure to convert the data.
302 : */
303 0 : value = ReceiveFunctionCall(&my_extra->proc,
304 : buf,
305 : my_extra->typioparam,
306 : my_extra->typtypmod);
307 :
308 : /*
309 : * Do the necessary checks to ensure it's a valid domain value.
310 : */
311 0 : domain_check_input(value, (buf == NULL), my_extra);
312 :
313 0 : if (buf == NULL)
314 0 : PG_RETURN_NULL();
315 : else
316 0 : PG_RETURN_DATUM(value);
317 : }
318 :
319 : /*
320 : * domain_check - check that a datum satisfies the constraints of a
321 : * domain. extra and mcxt can be passed if they are available from,
322 : * say, a FmgrInfo structure, or they can be NULL, in which case the
323 : * setup is repeated for each call.
324 : */
325 : void
326 448 : domain_check(Datum value, bool isnull, Oid domainType,
327 : void **extra, MemoryContext mcxt)
328 : {
329 448 : DomainIOData *my_extra = NULL;
330 :
331 448 : if (mcxt == NULL)
332 0 : mcxt = CurrentMemoryContext;
333 :
334 : /*
335 : * We arrange to look up the needed info just once per series of calls,
336 : * assuming the domain type doesn't change underneath us (which really
337 : * shouldn't happen, but cope if it does).
338 : */
339 448 : if (extra)
340 448 : my_extra = (DomainIOData *) *extra;
341 448 : if (my_extra == NULL || my_extra->domain_type != domainType)
342 : {
343 312 : my_extra = domain_state_setup(domainType, true, mcxt);
344 312 : if (extra)
345 312 : *extra = (void *) my_extra;
346 : }
347 :
348 : /*
349 : * Do the necessary checks to ensure it's a valid domain value.
350 : */
351 448 : domain_check_input(value, isnull, my_extra);
352 440 : }
353 :
354 : /*
355 : * errdatatype --- stores schema_name and datatype_name of a datatype
356 : * within the current errordata.
357 : */
358 : int
359 60 : errdatatype(Oid datatypeOid)
360 : {
361 : HeapTuple tup;
362 : Form_pg_type typtup;
363 :
364 60 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(datatypeOid));
365 60 : if (!HeapTupleIsValid(tup))
366 0 : elog(ERROR, "cache lookup failed for type %u", datatypeOid);
367 60 : typtup = (Form_pg_type) GETSTRUCT(tup);
368 :
369 60 : err_generic_string(PG_DIAG_SCHEMA_NAME,
370 60 : get_namespace_name(typtup->typnamespace));
371 60 : err_generic_string(PG_DIAG_DATATYPE_NAME, NameStr(typtup->typname));
372 :
373 60 : ReleaseSysCache(tup);
374 :
375 60 : return 0; /* return value does not matter */
376 : }
377 :
378 : /*
379 : * errdomainconstraint --- stores schema_name, datatype_name and
380 : * constraint_name of a domain-related constraint within the current errordata.
381 : */
382 : int
383 42 : errdomainconstraint(Oid datatypeOid, const char *conname)
384 : {
385 42 : errdatatype(datatypeOid);
386 42 : err_generic_string(PG_DIAG_CONSTRAINT_NAME, conname);
387 :
388 42 : return 0; /* return value does not matter */
389 : }
|