LCOV - code coverage report
Current view: top level - src/backend/utils/adt - domains.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 78 100 78.0 %
Date: 2017-09-29 13:40:31 Functions: 6 7 85.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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-2017, 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         361 : domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
      74             : {
      75             :     DomainIOData *my_extra;
      76             :     TypeCacheEntry *typentry;
      77             :     Oid         baseType;
      78             : 
      79         361 :     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.
      86             :      */
      87         361 :     typentry = lookup_type_cache(domainType, 0);
      88         361 :     if (typentry->typtype != TYPTYPE_DOMAIN)
      89           0 :         ereport(ERROR,
      90             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
      91             :                  errmsg("type %s is not a domain",
      92             :                         format_type_be(domainType))));
      93             : 
      94             :     /* Find out the base type */
      95         361 :     my_extra->typtypmod = -1;
      96         361 :     baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
      97             : 
      98             :     /* Look up underlying I/O function */
      99         361 :     if (binary)
     100         312 :         getTypeBinaryInputInfo(baseType,
     101             :                                &my_extra->typiofunc,
     102             :                                &my_extra->typioparam);
     103             :     else
     104          49 :         getTypeInputInfo(baseType,
     105             :                          &my_extra->typiofunc,
     106             :                          &my_extra->typioparam);
     107         361 :     fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
     108             : 
     109             :     /* Look up constraints for domain */
     110         361 :     InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt, true);
     111             : 
     112             :     /* We don't make an ExprContext until needed */
     113         361 :     my_extra->econtext = NULL;
     114         361 :     my_extra->mcxt = mcxt;
     115             : 
     116             :     /* Mark cache valid */
     117         361 :     my_extra->domain_type = domainType;
     118             : 
     119         361 :     return my_extra;
     120             : }
     121             : 
     122             : /*
     123             :  * domain_check_input - apply the cached checks.
     124             :  *
     125             :  * This is roughly similar to the handling of CoerceToDomain nodes in
     126             :  * execExpr*.c, but we execute each constraint separately, rather than
     127             :  * compiling them in-line within a larger expression.
     128             :  */
     129             : static void
     130        4529 : domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
     131             : {
     132        4529 :     ExprContext *econtext = my_extra->econtext;
     133             :     ListCell   *l;
     134             : 
     135             :     /* Make sure we have up-to-date constraints */
     136        4529 :     UpdateDomainConstraintRef(&my_extra->constraint_ref);
     137             : 
     138        5667 :     foreach(l, my_extra->constraint_ref.constraints)
     139             :     {
     140        1148 :         DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
     141             : 
     142        1148 :         switch (con->constrainttype)
     143             :         {
     144             :             case DOM_CONSTRAINT_NOTNULL:
     145          22 :                 if (isnull)
     146           6 :                     ereport(ERROR,
     147             :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
     148             :                              errmsg("domain %s does not allow null values",
     149             :                                     format_type_be(my_extra->domain_type)),
     150             :                              errdatatype(my_extra->domain_type)));
     151          16 :                 break;
     152             :             case DOM_CONSTRAINT_CHECK:
     153             :                 {
     154             :                     /* Make the econtext if we didn't already */
     155        1126 :                     if (econtext == NULL)
     156             :                     {
     157             :                         MemoryContext oldcontext;
     158             : 
     159         318 :                         oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
     160         318 :                         econtext = CreateStandaloneExprContext();
     161         318 :                         MemoryContextSwitchTo(oldcontext);
     162         318 :                         my_extra->econtext = econtext;
     163             :                     }
     164             : 
     165             :                     /*
     166             :                      * Set up value to be returned by CoerceToDomainValue
     167             :                      * nodes.  Unlike in the generic expression case, this
     168             :                      * econtext couldn't be shared with anything else, so no
     169             :                      * need to save and restore fields.  But we do need to
     170             :                      * protect the passed-in value against being changed by
     171             :                      * called functions.  (It couldn't be a R/W expanded
     172             :                      * object for most uses, but that seems possible for
     173             :                      * domain_check().)
     174             :                      */
     175        1126 :                     econtext->domainValue_datum =
     176        1126 :                         MakeExpandedObjectReadOnly(value, isnull,
     177             :                                                    my_extra->constraint_ref.tcache->typlen);
     178        1126 :                     econtext->domainValue_isNull = isnull;
     179             : 
     180        1126 :                     if (!ExecCheck(con->check_exprstate, econtext))
     181           4 :                         ereport(ERROR,
     182             :                                 (errcode(ERRCODE_CHECK_VIOLATION),
     183             :                                  errmsg("value for domain %s violates check constraint \"%s\"",
     184             :                                         format_type_be(my_extra->domain_type),
     185             :                                         con->name),
     186             :                                  errdomainconstraint(my_extra->domain_type,
     187             :                                                      con->name)));
     188        1122 :                     break;
     189             :                 }
     190             :             default:
     191           0 :                 elog(ERROR, "unrecognized constraint type: %d",
     192             :                      (int) con->constrainttype);
     193             :                 break;
     194             :         }
     195             :     }
     196             : 
     197             :     /*
     198             :      * Before exiting, call any shutdown callbacks and reset econtext's
     199             :      * per-tuple memory.  This avoids leaking non-memory resources, if
     200             :      * anything in the expression(s) has any.
     201             :      */
     202        4519 :     if (econtext)
     203        1122 :         ReScanExprContext(econtext);
     204        4519 : }
     205             : 
     206             : 
     207             : /*
     208             :  * domain_in        - input routine for any domain type.
     209             :  */
     210             : Datum
     211        4083 : domain_in(PG_FUNCTION_ARGS)
     212             : {
     213             :     char       *string;
     214             :     Oid         domainType;
     215             :     DomainIOData *my_extra;
     216             :     Datum       value;
     217             : 
     218             :     /*
     219             :      * Since domain_in is not strict, we have to check for null inputs. The
     220             :      * typioparam argument should never be null in normal system usage, but it
     221             :      * could be null in a manual invocation --- if so, just return null.
     222             :      */
     223        4083 :     if (PG_ARGISNULL(0))
     224          22 :         string = NULL;
     225             :     else
     226        4061 :         string = PG_GETARG_CSTRING(0);
     227        4083 :     if (PG_ARGISNULL(1))
     228           0 :         PG_RETURN_NULL();
     229        4083 :     domainType = PG_GETARG_OID(1);
     230             : 
     231             :     /*
     232             :      * We arrange to look up the needed info just once per series of calls,
     233             :      * assuming the domain type doesn't change underneath us (which really
     234             :      * shouldn't happen, but cope if it does).
     235             :      */
     236        4083 :     my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
     237        4083 :     if (my_extra == NULL || my_extra->domain_type != domainType)
     238             :     {
     239          49 :         my_extra = domain_state_setup(domainType, false,
     240          49 :                                       fcinfo->flinfo->fn_mcxt);
     241          49 :         fcinfo->flinfo->fn_extra = (void *) my_extra;
     242             :     }
     243             : 
     244             :     /*
     245             :      * Invoke the base type's typinput procedure to convert the data.
     246             :      */
     247        4083 :     value = InputFunctionCall(&my_extra->proc,
     248             :                               string,
     249             :                               my_extra->typioparam,
     250             :                               my_extra->typtypmod);
     251             : 
     252             :     /*
     253             :      * Do the necessary checks to ensure it's a valid domain value.
     254             :      */
     255        4081 :     domain_check_input(value, (string == NULL), my_extra);
     256             : 
     257        4079 :     if (string == NULL)
     258          20 :         PG_RETURN_NULL();
     259             :     else
     260        4059 :         PG_RETURN_DATUM(value);
     261             : }
     262             : 
     263             : /*
     264             :  * domain_recv      - binary input routine for any domain type.
     265             :  */
     266             : Datum
     267           0 : domain_recv(PG_FUNCTION_ARGS)
     268             : {
     269             :     StringInfo  buf;
     270             :     Oid         domainType;
     271             :     DomainIOData *my_extra;
     272             :     Datum       value;
     273             : 
     274             :     /*
     275             :      * Since domain_recv is not strict, we have to check for null inputs. The
     276             :      * typioparam argument should never be null in normal system usage, but it
     277             :      * could be null in a manual invocation --- if so, just return null.
     278             :      */
     279           0 :     if (PG_ARGISNULL(0))
     280           0 :         buf = NULL;
     281             :     else
     282           0 :         buf = (StringInfo) PG_GETARG_POINTER(0);
     283           0 :     if (PG_ARGISNULL(1))
     284           0 :         PG_RETURN_NULL();
     285           0 :     domainType = PG_GETARG_OID(1);
     286             : 
     287             :     /*
     288             :      * We arrange to look up the needed info just once per series of calls,
     289             :      * assuming the domain type doesn't change underneath us (which really
     290             :      * shouldn't happen, but cope if it does).
     291             :      */
     292           0 :     my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
     293           0 :     if (my_extra == NULL || my_extra->domain_type != domainType)
     294             :     {
     295           0 :         my_extra = domain_state_setup(domainType, true,
     296           0 :                                       fcinfo->flinfo->fn_mcxt);
     297           0 :         fcinfo->flinfo->fn_extra = (void *) my_extra;
     298             :     }
     299             : 
     300             :     /*
     301             :      * Invoke the base type's typreceive procedure to convert the data.
     302             :      */
     303           0 :     value = ReceiveFunctionCall(&my_extra->proc,
     304             :                                 buf,
     305             :                                 my_extra->typioparam,
     306             :                                 my_extra->typtypmod);
     307             : 
     308             :     /*
     309             :      * Do the necessary checks to ensure it's a valid domain value.
     310             :      */
     311           0 :     domain_check_input(value, (buf == NULL), my_extra);
     312             : 
     313           0 :     if (buf == NULL)
     314           0 :         PG_RETURN_NULL();
     315             :     else
     316           0 :         PG_RETURN_DATUM(value);
     317             : }
     318             : 
     319             : /*
     320             :  * domain_check - check that a datum satisfies the constraints of a
     321             :  * domain.  extra and mcxt can be passed if they are available from,
     322             :  * say, a FmgrInfo structure, or they can be NULL, in which case the
     323             :  * setup is repeated for each call.
     324             :  */
     325             : void
     326         448 : domain_check(Datum value, bool isnull, Oid domainType,
     327             :              void **extra, MemoryContext mcxt)
     328             : {
     329         448 :     DomainIOData *my_extra = NULL;
     330             : 
     331         448 :     if (mcxt == NULL)
     332           0 :         mcxt = CurrentMemoryContext;
     333             : 
     334             :     /*
     335             :      * We arrange to look up the needed info just once per series of calls,
     336             :      * assuming the domain type doesn't change underneath us (which really
     337             :      * shouldn't happen, but cope if it does).
     338             :      */
     339         448 :     if (extra)
     340         448 :         my_extra = (DomainIOData *) *extra;
     341         448 :     if (my_extra == NULL || my_extra->domain_type != domainType)
     342             :     {
     343         312 :         my_extra = domain_state_setup(domainType, true, mcxt);
     344         312 :         if (extra)
     345         312 :             *extra = (void *) my_extra;
     346             :     }
     347             : 
     348             :     /*
     349             :      * Do the necessary checks to ensure it's a valid domain value.
     350             :      */
     351         448 :     domain_check_input(value, isnull, my_extra);
     352         440 : }
     353             : 
     354             : /*
     355             :  * errdatatype --- stores schema_name and datatype_name of a datatype
     356             :  * within the current errordata.
     357             :  */
     358             : int
     359          60 : errdatatype(Oid datatypeOid)
     360             : {
     361             :     HeapTuple   tup;
     362             :     Form_pg_type typtup;
     363             : 
     364          60 :     tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(datatypeOid));
     365          60 :     if (!HeapTupleIsValid(tup))
     366           0 :         elog(ERROR, "cache lookup failed for type %u", datatypeOid);
     367          60 :     typtup = (Form_pg_type) GETSTRUCT(tup);
     368             : 
     369          60 :     err_generic_string(PG_DIAG_SCHEMA_NAME,
     370          60 :                        get_namespace_name(typtup->typnamespace));
     371          60 :     err_generic_string(PG_DIAG_DATATYPE_NAME, NameStr(typtup->typname));
     372             : 
     373          60 :     ReleaseSysCache(tup);
     374             : 
     375          60 :     return 0;                   /* return value does not matter */
     376             : }
     377             : 
     378             : /*
     379             :  * errdomainconstraint --- stores schema_name, datatype_name and
     380             :  * constraint_name of a domain-related constraint within the current errordata.
     381             :  */
     382             : int
     383          42 : errdomainconstraint(Oid datatypeOid, const char *conname)
     384             : {
     385          42 :     errdatatype(datatypeOid);
     386          42 :     err_generic_string(PG_DIAG_CONSTRAINT_NAME, conname);
     387             : 
     388          42 :     return 0;                   /* return value does not matter */
     389             : }

Generated by: LCOV version 1.11