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