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