1 /*-------------------------------------------------------------------------
4 * I/O functions for domain types.
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.
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.
23 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
24 * Portions Copyright (c) 1994, Regents of the University of California
28 * $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.7 2008/06/06 22:35:22 alvherre Exp $
30 *-------------------------------------------------------------------------
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"
42 * structure to cache state across multiple calls
44 typedef struct DomainIOData
47 /* Data needed to call base type's input function */
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 */
62 * domain_state_setup - initialize the cache for a new domain type.
65 domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary,
69 MemoryContext oldcontext;
71 /* Mark cache invalid */
72 my_extra->domain_type = InvalidOid;
74 /* Find out the base type */
75 my_extra->typtypmod = -1;
76 baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
77 if (baseType == domainType)
79 (errcode(ERRCODE_DATATYPE_MISMATCH),
80 errmsg("type %s is not a domain",
81 format_type_be(domainType))));
83 /* Look up underlying I/O function */
85 getTypeBinaryInputInfo(baseType,
87 &my_extra->typioparam);
89 getTypeInputInfo(baseType,
91 &my_extra->typioparam);
92 fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
94 /* Look up constraints for domain */
95 oldcontext = MemoryContextSwitchTo(mcxt);
96 my_extra->constraint_list = GetDomainConstraints(domainType);
97 MemoryContextSwitchTo(oldcontext);
99 /* We don't make an ExprContext until needed */
100 my_extra->econtext = NULL;
101 my_extra->mcxt = mcxt;
103 /* Mark cache valid */
104 my_extra->domain_type = domainType;
108 * domain_check_input - apply the cached checks.
110 * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
113 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
115 ExprContext *econtext = my_extra->econtext;
118 foreach(l, my_extra->constraint_list)
120 DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
122 switch (con->constrainttype)
124 case DOM_CONSTRAINT_NOTNULL:
127 (errcode(ERRCODE_NOT_NULL_VIOLATION),
128 errmsg("domain %s does not allow null values",
129 format_type_be(my_extra->domain_type))));
131 case DOM_CONSTRAINT_CHECK:
136 /* Make the econtext if we didn't already */
137 if (econtext == NULL)
139 MemoryContext oldcontext;
141 oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
142 econtext = CreateStandaloneExprContext();
143 MemoryContextSwitchTo(oldcontext);
144 my_extra->econtext = econtext;
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.
153 econtext->domainValue_datum = value;
154 econtext->domainValue_isNull = isnull;
156 conResult = ExecEvalExprSwitchContext(con->check_expr,
161 !DatumGetBool(conResult))
163 (errcode(ERRCODE_CHECK_VIOLATION),
164 errmsg("value for domain %s violates check constraint \"%s\"",
165 format_type_be(my_extra->domain_type),
170 elog(ERROR, "unrecognized constraint type: %d",
171 (int) con->constrainttype);
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.
182 ReScanExprContext(econtext);
187 * domain_in - input routine for any domain type.
190 domain_in(PG_FUNCTION_ARGS)
194 DomainIOData *my_extra;
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.
205 string = PG_GETARG_CSTRING(0);
208 domainType = PG_GETARG_OID(1);
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.
214 my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
215 if (my_extra == NULL)
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;
223 else if (my_extra->domain_type != domainType)
224 domain_state_setup(my_extra, domainType, false,
225 fcinfo->flinfo->fn_mcxt);
228 * Invoke the base type's typinput procedure to convert the data.
230 value = InputFunctionCall(&my_extra->proc,
232 my_extra->typioparam,
233 my_extra->typtypmod);
236 * Do the necessary checks to ensure it's a valid domain value.
238 domain_check_input(value, (string == NULL), my_extra);
243 PG_RETURN_DATUM(value);
247 * domain_recv - binary input routine for any domain type.
250 domain_recv(PG_FUNCTION_ARGS)
254 DomainIOData *my_extra;
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.
265 buf = (StringInfo) PG_GETARG_POINTER(0);
268 domainType = PG_GETARG_OID(1);
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.
274 my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
275 if (my_extra == NULL)
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;
283 else if (my_extra->domain_type != domainType)
284 domain_state_setup(my_extra, domainType, true,
285 fcinfo->flinfo->fn_mcxt);
288 * Invoke the base type's typreceive procedure to convert the data.
290 value = ReceiveFunctionCall(&my_extra->proc,
292 my_extra->typioparam,
293 my_extra->typtypmod);
296 * Do the necessary checks to ensure it's a valid domain value.
298 domain_check_input(value, (buf == NULL), my_extra);
303 PG_RETURN_DATUM(value);