Header And Logo

PostgreSQL
| The world's most advanced open source database.

domains.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * domains.c
00004  *    I/O functions for domain types.
00005  *
00006  * The output functions for a domain type are just the same ones provided
00007  * by its underlying base type.  The input functions, however, must be
00008  * prepared to apply any constraints defined by the type.  So, we create
00009  * special input functions that invoke the base type's input function
00010  * and then check the constraints.
00011  *
00012  * The overhead required for constraint checking can be high, since examining
00013  * the catalogs to discover the constraints for a given domain is not cheap.
00014  * We have three mechanisms for minimizing this cost:
00015  *  1.  In a nest of domains, we flatten the checking of all the levels
00016  *      into just one operation.
00017  *  2.  We cache the list of constraint items in the FmgrInfo struct
00018  *      passed by the caller.
00019  *  3.  If there are CHECK constraints, we cache a standalone ExprContext
00020  *      to evaluate them in.
00021  *
00022  *
00023  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00024  * Portions Copyright (c) 1994, Regents of the University of California
00025  *
00026  *
00027  * IDENTIFICATION
00028  *    src/backend/utils/adt/domains.c
00029  *
00030  *-------------------------------------------------------------------------
00031  */
00032 #include "postgres.h"
00033 
00034 #include "access/htup_details.h"
00035 #include "catalog/pg_type.h"
00036 #include "commands/typecmds.h"
00037 #include "executor/executor.h"
00038 #include "lib/stringinfo.h"
00039 #include "utils/builtins.h"
00040 #include "utils/lsyscache.h"
00041 #include "utils/syscache.h"
00042 
00043 
00044 /*
00045  * structure to cache state across multiple calls
00046  */
00047 typedef struct DomainIOData
00048 {
00049     Oid         domain_type;
00050     /* Data needed to call base type's input function */
00051     Oid         typiofunc;
00052     Oid         typioparam;
00053     int32       typtypmod;
00054     FmgrInfo    proc;
00055     /* List of constraint items to check */
00056     List       *constraint_list;
00057     /* Context for evaluating CHECK constraints in */
00058     ExprContext *econtext;
00059     /* Memory context this cache is in */
00060     MemoryContext mcxt;
00061 } DomainIOData;
00062 
00063 
00064 /*
00065  * domain_state_setup - initialize the cache for a new domain type.
00066  */
00067 static void
00068 domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary,
00069                    MemoryContext mcxt)
00070 {
00071     Oid         baseType;
00072     MemoryContext oldcontext;
00073 
00074     /* Mark cache invalid */
00075     my_extra->domain_type = InvalidOid;
00076 
00077     /* Find out the base type */
00078     my_extra->typtypmod = -1;
00079     baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
00080     if (baseType == domainType)
00081         ereport(ERROR,
00082                 (errcode(ERRCODE_DATATYPE_MISMATCH),
00083                  errmsg("type %s is not a domain",
00084                         format_type_be(domainType))));
00085 
00086     /* Look up underlying I/O function */
00087     if (binary)
00088         getTypeBinaryInputInfo(baseType,
00089                                &my_extra->typiofunc,
00090                                &my_extra->typioparam);
00091     else
00092         getTypeInputInfo(baseType,
00093                          &my_extra->typiofunc,
00094                          &my_extra->typioparam);
00095     fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
00096 
00097     /* Look up constraints for domain */
00098     oldcontext = MemoryContextSwitchTo(mcxt);
00099     my_extra->constraint_list = GetDomainConstraints(domainType);
00100     MemoryContextSwitchTo(oldcontext);
00101 
00102     /* We don't make an ExprContext until needed */
00103     my_extra->econtext = NULL;
00104     my_extra->mcxt = mcxt;
00105 
00106     /* Mark cache valid */
00107     my_extra->domain_type = domainType;
00108 }
00109 
00110 /*
00111  * domain_check_input - apply the cached checks.
00112  *
00113  * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
00114  */
00115 static void
00116 domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
00117 {
00118     ExprContext *econtext = my_extra->econtext;
00119     ListCell   *l;
00120 
00121     foreach(l, my_extra->constraint_list)
00122     {
00123         DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
00124 
00125         switch (con->constrainttype)
00126         {
00127             case DOM_CONSTRAINT_NOTNULL:
00128                 if (isnull)
00129                     ereport(ERROR,
00130                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
00131                              errmsg("domain %s does not allow null values",
00132                                     format_type_be(my_extra->domain_type)),
00133                              errdatatype(my_extra->domain_type)));
00134                 break;
00135             case DOM_CONSTRAINT_CHECK:
00136                 {
00137                     Datum       conResult;
00138                     bool        conIsNull;
00139 
00140                     /* Make the econtext if we didn't already */
00141                     if (econtext == NULL)
00142                     {
00143                         MemoryContext oldcontext;
00144 
00145                         oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
00146                         econtext = CreateStandaloneExprContext();
00147                         MemoryContextSwitchTo(oldcontext);
00148                         my_extra->econtext = econtext;
00149                     }
00150 
00151                     /*
00152                      * Set up value to be returned by CoerceToDomainValue
00153                      * nodes.  Unlike ExecEvalCoerceToDomain, this econtext
00154                      * couldn't be shared with anything else, so no need to
00155                      * save and restore fields.
00156                      */
00157                     econtext->domainValue_datum = value;
00158                     econtext->domainValue_isNull = isnull;
00159 
00160                     conResult = ExecEvalExprSwitchContext(con->check_expr,
00161                                                           econtext,
00162                                                           &conIsNull, NULL);
00163 
00164                     if (!conIsNull &&
00165                         !DatumGetBool(conResult))
00166                         ereport(ERROR,
00167                                 (errcode(ERRCODE_CHECK_VIOLATION),
00168                                  errmsg("value for domain %s violates check constraint \"%s\"",
00169                                         format_type_be(my_extra->domain_type),
00170                                         con->name),
00171                                  errdomainconstraint(my_extra->domain_type,
00172                                                      con->name)));
00173                     break;
00174                 }
00175             default:
00176                 elog(ERROR, "unrecognized constraint type: %d",
00177                      (int) con->constrainttype);
00178                 break;
00179         }
00180     }
00181 
00182     /*
00183      * Before exiting, call any shutdown callbacks and reset econtext's
00184      * per-tuple memory.  This avoids leaking non-memory resources, if
00185      * anything in the expression(s) has any.
00186      */
00187     if (econtext)
00188         ReScanExprContext(econtext);
00189 }
00190 
00191 
00192 /*
00193  * domain_in        - input routine for any domain type.
00194  */
00195 Datum
00196 domain_in(PG_FUNCTION_ARGS)
00197 {
00198     char       *string;
00199     Oid         domainType;
00200     DomainIOData *my_extra;
00201     Datum       value;
00202 
00203     /*
00204      * Since domain_in is not strict, we have to check for null inputs. The
00205      * typioparam argument should never be null in normal system usage, but it
00206      * could be null in a manual invocation --- if so, just return null.
00207      */
00208     if (PG_ARGISNULL(0))
00209         string = NULL;
00210     else
00211         string = PG_GETARG_CSTRING(0);
00212     if (PG_ARGISNULL(1))
00213         PG_RETURN_NULL();
00214     domainType = PG_GETARG_OID(1);
00215 
00216     /*
00217      * We arrange to look up the needed info just once per series of calls,
00218      * assuming the domain type doesn't change underneath us.
00219      */
00220     my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
00221     if (my_extra == NULL)
00222     {
00223         my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00224                                                        sizeof(DomainIOData));
00225         domain_state_setup(my_extra, domainType, false,
00226                            fcinfo->flinfo->fn_mcxt);
00227         fcinfo->flinfo->fn_extra = (void *) my_extra;
00228     }
00229     else if (my_extra->domain_type != domainType)
00230         domain_state_setup(my_extra, domainType, false,
00231                            fcinfo->flinfo->fn_mcxt);
00232 
00233     /*
00234      * Invoke the base type's typinput procedure to convert the data.
00235      */
00236     value = InputFunctionCall(&my_extra->proc,
00237                               string,
00238                               my_extra->typioparam,
00239                               my_extra->typtypmod);
00240 
00241     /*
00242      * Do the necessary checks to ensure it's a valid domain value.
00243      */
00244     domain_check_input(value, (string == NULL), my_extra);
00245 
00246     if (string == NULL)
00247         PG_RETURN_NULL();
00248     else
00249         PG_RETURN_DATUM(value);
00250 }
00251 
00252 /*
00253  * domain_recv      - binary input routine for any domain type.
00254  */
00255 Datum
00256 domain_recv(PG_FUNCTION_ARGS)
00257 {
00258     StringInfo  buf;
00259     Oid         domainType;
00260     DomainIOData *my_extra;
00261     Datum       value;
00262 
00263     /*
00264      * Since domain_recv is not strict, we have to check for null inputs. The
00265      * typioparam argument should never be null in normal system usage, but it
00266      * could be null in a manual invocation --- if so, just return null.
00267      */
00268     if (PG_ARGISNULL(0))
00269         buf = NULL;
00270     else
00271         buf = (StringInfo) PG_GETARG_POINTER(0);
00272     if (PG_ARGISNULL(1))
00273         PG_RETURN_NULL();
00274     domainType = PG_GETARG_OID(1);
00275 
00276     /*
00277      * We arrange to look up the needed info just once per series of calls,
00278      * assuming the domain type doesn't change underneath us.
00279      */
00280     my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
00281     if (my_extra == NULL)
00282     {
00283         my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00284                                                        sizeof(DomainIOData));
00285         domain_state_setup(my_extra, domainType, true,
00286                            fcinfo->flinfo->fn_mcxt);
00287         fcinfo->flinfo->fn_extra = (void *) my_extra;
00288     }
00289     else if (my_extra->domain_type != domainType)
00290         domain_state_setup(my_extra, domainType, true,
00291                            fcinfo->flinfo->fn_mcxt);
00292 
00293     /*
00294      * Invoke the base type's typreceive procedure to convert the data.
00295      */
00296     value = ReceiveFunctionCall(&my_extra->proc,
00297                                 buf,
00298                                 my_extra->typioparam,
00299                                 my_extra->typtypmod);
00300 
00301     /*
00302      * Do the necessary checks to ensure it's a valid domain value.
00303      */
00304     domain_check_input(value, (buf == NULL), my_extra);
00305 
00306     if (buf == NULL)
00307         PG_RETURN_NULL();
00308     else
00309         PG_RETURN_DATUM(value);
00310 }
00311 
00312 /*
00313  * domain_check - check that a datum satisfies the constraints of a
00314  * domain.  extra and mcxt can be passed if they are available from,
00315  * say, a FmgrInfo structure, or they can be NULL, in which case the
00316  * setup is repeated for each call.
00317  */
00318 void
00319 domain_check(Datum value, bool isnull, Oid domainType,
00320              void **extra, MemoryContext mcxt)
00321 {
00322     DomainIOData *my_extra = NULL;
00323 
00324     if (mcxt == NULL)
00325         mcxt = CurrentMemoryContext;
00326 
00327     /*
00328      * We arrange to look up the needed info just once per series of calls,
00329      * assuming the domain type doesn't change underneath us.
00330      */
00331     if (extra)
00332         my_extra = (DomainIOData *) *extra;
00333     if (my_extra == NULL)
00334     {
00335         my_extra = (DomainIOData *) MemoryContextAlloc(mcxt,
00336                                                        sizeof(DomainIOData));
00337         domain_state_setup(my_extra, domainType, true, mcxt);
00338         if (extra)
00339             *extra = (void *) my_extra;
00340     }
00341     else if (my_extra->domain_type != domainType)
00342         domain_state_setup(my_extra, domainType, true, mcxt);
00343 
00344     /*
00345      * Do the necessary checks to ensure it's a valid domain value.
00346      */
00347     domain_check_input(value, isnull, my_extra);
00348 }
00349 
00350 /*
00351  * errdatatype --- stores schema_name and datatype_name of a datatype
00352  * within the current errordata.
00353  */
00354 int
00355 errdatatype(Oid datatypeOid)
00356 {
00357     HeapTuple   tup;
00358     Form_pg_type typtup;
00359 
00360     tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(datatypeOid));
00361     if (!HeapTupleIsValid(tup))
00362         elog(ERROR, "cache lookup failed for type %u", datatypeOid);
00363     typtup = (Form_pg_type) GETSTRUCT(tup);
00364 
00365     err_generic_string(PG_DIAG_SCHEMA_NAME,
00366                        get_namespace_name(typtup->typnamespace));
00367     err_generic_string(PG_DIAG_DATATYPE_NAME, NameStr(typtup->typname));
00368 
00369     ReleaseSysCache(tup);
00370 
00371     return 0;                   /* return value does not matter */
00372 }
00373 
00374 /*
00375  * errdomainconstraint --- stores schema_name, datatype_name and
00376  * constraint_name of a domain-related constraint within the current errordata.
00377  */
00378 int
00379 errdomainconstraint(Oid datatypeOid, const char *conname)
00380 {
00381     errdatatype(datatypeOid);
00382     err_generic_string(PG_DIAG_CONSTRAINT_NAME, conname);
00383 
00384     return 0;                   /* return value does not matter */
00385 }