]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/domains.c
Fix initialization of fake LSN for unlogged relations
[postgresql] / src / backend / utils / adt / domains.c
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-2019, 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 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 static void
131 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
132 {
133         ExprContext *econtext = my_extra->econtext;
134         ListCell   *l;
135
136         /* Make sure we have up-to-date constraints */
137         UpdateDomainConstraintRef(&my_extra->constraint_ref);
138
139         foreach(l, my_extra->constraint_ref.constraints)
140         {
141                 DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
142
143                 switch (con->constrainttype)
144                 {
145                         case DOM_CONSTRAINT_NOTNULL:
146                                 if (isnull)
147                                         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                                 break;
153                         case DOM_CONSTRAINT_CHECK:
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 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                                         econtext->domainValue_datum =
177                                                 MakeExpandedObjectReadOnly(value, isnull,
178                                                                                                    my_extra->constraint_ref.tcache->typlen);
179                                         econtext->domainValue_isNull = isnull;
180
181                                         if (!ExecCheck(con->check_exprstate, econtext))
182                                                 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                                         break;
190                                 }
191                         default:
192                                 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         if (econtext)
204                 ReScanExprContext(econtext);
205 }
206
207
208 /*
209  * domain_in            - input routine for any domain type.
210  */
211 Datum
212 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         if (PG_ARGISNULL(0))
225                 string = NULL;
226         else
227                 string = PG_GETARG_CSTRING(0);
228         if (PG_ARGISNULL(1))
229                 PG_RETURN_NULL();
230         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         my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
238         if (my_extra == NULL || my_extra->domain_type != domainType)
239         {
240                 my_extra = domain_state_setup(domainType, false,
241                                                                           fcinfo->flinfo->fn_mcxt);
242                 fcinfo->flinfo->fn_extra = (void *) my_extra;
243         }
244
245         /*
246          * Invoke the base type's typinput procedure to convert the data.
247          */
248         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         domain_check_input(value, (string == NULL), my_extra);
257
258         if (string == NULL)
259                 PG_RETURN_NULL();
260         else
261                 PG_RETURN_DATUM(value);
262 }
263
264 /*
265  * domain_recv          - binary input routine for any domain type.
266  */
267 Datum
268 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         if (PG_ARGISNULL(0))
281                 buf = NULL;
282         else
283                 buf = (StringInfo) PG_GETARG_POINTER(0);
284         if (PG_ARGISNULL(1))
285                 PG_RETURN_NULL();
286         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         my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
294         if (my_extra == NULL || my_extra->domain_type != domainType)
295         {
296                 my_extra = domain_state_setup(domainType, true,
297                                                                           fcinfo->flinfo->fn_mcxt);
298                 fcinfo->flinfo->fn_extra = (void *) my_extra;
299         }
300
301         /*
302          * Invoke the base type's typreceive procedure to convert the data.
303          */
304         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         domain_check_input(value, (buf == NULL), my_extra);
313
314         if (buf == NULL)
315                 PG_RETURN_NULL();
316         else
317                 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 domain_check(Datum value, bool isnull, Oid domainType,
328                          void **extra, MemoryContext mcxt)
329 {
330         DomainIOData *my_extra = NULL;
331
332         if (mcxt == NULL)
333                 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         if (extra)
341                 my_extra = (DomainIOData *) *extra;
342         if (my_extra == NULL || my_extra->domain_type != domainType)
343         {
344                 my_extra = domain_state_setup(domainType, true, mcxt);
345                 if (extra)
346                         *extra = (void *) my_extra;
347         }
348
349         /*
350          * Do the necessary checks to ensure it's a valid domain value.
351          */
352         domain_check_input(value, isnull, my_extra);
353 }
354
355 /*
356  * errdatatype --- stores schema_name and datatype_name of a datatype
357  * within the current errordata.
358  */
359 int
360 errdatatype(Oid datatypeOid)
361 {
362         HeapTuple       tup;
363         Form_pg_type typtup;
364
365         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(datatypeOid));
366         if (!HeapTupleIsValid(tup))
367                 elog(ERROR, "cache lookup failed for type %u", datatypeOid);
368         typtup = (Form_pg_type) GETSTRUCT(tup);
369
370         err_generic_string(PG_DIAG_SCHEMA_NAME,
371                                            get_namespace_name(typtup->typnamespace));
372         err_generic_string(PG_DIAG_DATATYPE_NAME, NameStr(typtup->typname));
373
374         ReleaseSysCache(tup);
375
376         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 errdomainconstraint(Oid datatypeOid, const char *conname)
385 {
386         errdatatype(datatypeOid);
387         err_generic_string(PG_DIAG_CONSTRAINT_NAME, conname);
388
389         return 0;                                       /* return value does not matter */
390 }