]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/domains.c
Change xlog.h to xlogdefs.h in bufpage.h, and fix fallout.
[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.      In a nest of domains, we flatten the checking of all the levels
16  *              into just one operation.
17  *      2.      We cache the list of constraint items in the FmgrInfo struct
18  *              passed by the caller.
19  *      3.      If there are CHECK constraints, we cache a standalone ExprContext
20  *              to evaluate them in.
21  *
22  *
23  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
24  * Portions Copyright (c) 1994, Regents of the University of California
25  *
26  *
27  * IDENTIFICATION
28  *        $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.7 2008/06/06 22:35:22 alvherre Exp $
29  *
30  *-------------------------------------------------------------------------
31  */
32 #include "postgres.h"
33
34 #include "commands/typecmds.h"
35 #include "executor/executor.h"
36 #include "lib/stringinfo.h"
37 #include "utils/builtins.h"
38 #include "utils/lsyscache.h"
39
40
41 /*
42  * structure to cache state across multiple calls
43  */
44 typedef struct DomainIOData
45 {
46         Oid                     domain_type;
47         /* Data needed to call base type's input function */
48         Oid                     typiofunc;
49         Oid                     typioparam;
50         int32           typtypmod;
51         FmgrInfo        proc;
52         /* List of constraint items to check */
53         List       *constraint_list;
54         /* Context for evaluating CHECK constraints in */
55         ExprContext *econtext;
56         /* Memory context this cache is in */
57         MemoryContext mcxt;
58 } DomainIOData;
59
60
61 /*
62  * domain_state_setup - initialize the cache for a new domain type.
63  */
64 static void
65 domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary,
66                                    MemoryContext mcxt)
67 {
68         Oid                     baseType;
69         MemoryContext oldcontext;
70
71         /* Mark cache invalid */
72         my_extra->domain_type = InvalidOid;
73
74         /* Find out the base type */
75         my_extra->typtypmod = -1;
76         baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
77         if (baseType == domainType)
78                 ereport(ERROR,
79                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
80                                  errmsg("type %s is not a domain",
81                                                 format_type_be(domainType))));
82
83         /* Look up underlying I/O function */
84         if (binary)
85                 getTypeBinaryInputInfo(baseType,
86                                                            &my_extra->typiofunc,
87                                                            &my_extra->typioparam);
88         else
89                 getTypeInputInfo(baseType,
90                                                  &my_extra->typiofunc,
91                                                  &my_extra->typioparam);
92         fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
93
94         /* Look up constraints for domain */
95         oldcontext = MemoryContextSwitchTo(mcxt);
96         my_extra->constraint_list = GetDomainConstraints(domainType);
97         MemoryContextSwitchTo(oldcontext);
98
99         /* We don't make an ExprContext until needed */
100         my_extra->econtext = NULL;
101         my_extra->mcxt = mcxt;
102
103         /* Mark cache valid */
104         my_extra->domain_type = domainType;
105 }
106
107 /*
108  * domain_check_input - apply the cached checks.
109  *
110  * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
111  */
112 static void
113 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
114 {
115         ExprContext *econtext = my_extra->econtext;
116         ListCell   *l;
117
118         foreach(l, my_extra->constraint_list)
119         {
120                 DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
121
122                 switch (con->constrainttype)
123                 {
124                         case DOM_CONSTRAINT_NOTNULL:
125                                 if (isnull)
126                                         ereport(ERROR,
127                                                         (errcode(ERRCODE_NOT_NULL_VIOLATION),
128                                                          errmsg("domain %s does not allow null values",
129                                                                         format_type_be(my_extra->domain_type))));
130                                 break;
131                         case DOM_CONSTRAINT_CHECK:
132                                 {
133                                         Datum           conResult;
134                                         bool            conIsNull;
135
136                                         /* Make the econtext if we didn't already */
137                                         if (econtext == NULL)
138                                         {
139                                                 MemoryContext oldcontext;
140
141                                                 oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
142                                                 econtext = CreateStandaloneExprContext();
143                                                 MemoryContextSwitchTo(oldcontext);
144                                                 my_extra->econtext = econtext;
145                                         }
146
147                                         /*
148                                          * Set up value to be returned by CoerceToDomainValue
149                                          * nodes.  Unlike ExecEvalCoerceToDomain, this econtext
150                                          * couldn't be shared with anything else, so no need to
151                                          * save and restore fields.
152                                          */
153                                         econtext->domainValue_datum = value;
154                                         econtext->domainValue_isNull = isnull;
155
156                                         conResult = ExecEvalExprSwitchContext(con->check_expr,
157                                                                                                                   econtext,
158                                                                                                                   &conIsNull, NULL);
159
160                                         if (!conIsNull &&
161                                                 !DatumGetBool(conResult))
162                                                 ereport(ERROR,
163                                                                 (errcode(ERRCODE_CHECK_VIOLATION),
164                                                                  errmsg("value for domain %s violates check constraint \"%s\"",
165                                                                                 format_type_be(my_extra->domain_type),
166                                                                                 con->name)));
167                                         break;
168                                 }
169                         default:
170                                 elog(ERROR, "unrecognized constraint type: %d",
171                                          (int) con->constrainttype);
172                                 break;
173                 }
174         }
175
176         /*
177          * Before exiting, call any shutdown callbacks and reset econtext's
178          * per-tuple memory.  This avoids leaking non-memory resources, if
179          * anything in the expression(s) has any.
180          */
181         if (econtext)
182                 ReScanExprContext(econtext);
183 }
184
185
186 /*
187  * domain_in            - input routine for any domain type.
188  */
189 Datum
190 domain_in(PG_FUNCTION_ARGS)
191 {
192         char       *string;
193         Oid                     domainType;
194         DomainIOData *my_extra;
195         Datum           value;
196
197         /*
198          * Since domain_in is not strict, we have to check for null inputs. The
199          * typioparam argument should never be null in normal system usage, but it
200          * could be null in a manual invocation --- if so, just return null.
201          */
202         if (PG_ARGISNULL(0))
203                 string = NULL;
204         else
205                 string = PG_GETARG_CSTRING(0);
206         if (PG_ARGISNULL(1))
207                 PG_RETURN_NULL();
208         domainType = PG_GETARG_OID(1);
209
210         /*
211          * We arrange to look up the needed info just once per series of calls,
212          * assuming the domain type doesn't change underneath us.
213          */
214         my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
215         if (my_extra == NULL)
216         {
217                 my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
218                                                                                                            sizeof(DomainIOData));
219                 domain_state_setup(my_extra, domainType, false,
220                                                    fcinfo->flinfo->fn_mcxt);
221                 fcinfo->flinfo->fn_extra = (void *) my_extra;
222         }
223         else if (my_extra->domain_type != domainType)
224                 domain_state_setup(my_extra, domainType, false,
225                                                    fcinfo->flinfo->fn_mcxt);
226
227         /*
228          * Invoke the base type's typinput procedure to convert the data.
229          */
230         value = InputFunctionCall(&my_extra->proc,
231                                                           string,
232                                                           my_extra->typioparam,
233                                                           my_extra->typtypmod);
234
235         /*
236          * Do the necessary checks to ensure it's a valid domain value.
237          */
238         domain_check_input(value, (string == NULL), my_extra);
239
240         if (string == NULL)
241                 PG_RETURN_NULL();
242         else
243                 PG_RETURN_DATUM(value);
244 }
245
246 /*
247  * domain_recv          - binary input routine for any domain type.
248  */
249 Datum
250 domain_recv(PG_FUNCTION_ARGS)
251 {
252         StringInfo      buf;
253         Oid                     domainType;
254         DomainIOData *my_extra;
255         Datum           value;
256
257         /*
258          * Since domain_recv is not strict, we have to check for null inputs. The
259          * typioparam argument should never be null in normal system usage, but it
260          * could be null in a manual invocation --- if so, just return null.
261          */
262         if (PG_ARGISNULL(0))
263                 buf = NULL;
264         else
265                 buf = (StringInfo) PG_GETARG_POINTER(0);
266         if (PG_ARGISNULL(1))
267                 PG_RETURN_NULL();
268         domainType = PG_GETARG_OID(1);
269
270         /*
271          * We arrange to look up the needed info just once per series of calls,
272          * assuming the domain type doesn't change underneath us.
273          */
274         my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
275         if (my_extra == NULL)
276         {
277                 my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
278                                                                                                            sizeof(DomainIOData));
279                 domain_state_setup(my_extra, domainType, true,
280                                                    fcinfo->flinfo->fn_mcxt);
281                 fcinfo->flinfo->fn_extra = (void *) my_extra;
282         }
283         else if (my_extra->domain_type != domainType)
284                 domain_state_setup(my_extra, domainType, true,
285                                                    fcinfo->flinfo->fn_mcxt);
286
287         /*
288          * Invoke the base type's typreceive procedure to convert the data.
289          */
290         value = ReceiveFunctionCall(&my_extra->proc,
291                                                                 buf,
292                                                                 my_extra->typioparam,
293                                                                 my_extra->typtypmod);
294
295         /*
296          * Do the necessary checks to ensure it's a valid domain value.
297          */
298         domain_check_input(value, (buf == NULL), my_extra);
299
300         if (buf == NULL)
301                 PG_RETURN_NULL();
302         else
303                 PG_RETURN_DATUM(value);
304 }