diff options
| author | Tom Lane | 2006-04-05 22:11:58 +0000 |
|---|---|---|
| committer | Tom Lane | 2006-04-05 22:11:58 +0000 |
| commit | 7fdb4305db20f64bce27e6bac0a0f9c972e4dec8 (patch) | |
| tree | 18efa90dfcf996675cf1c7bb938d8575f7e551ad /src/backend | |
| parent | 89a67e523e744eb168b41d192b83d17a395b4137 (diff) | |
Fix a bunch of problems with domains by making them use special input functions
that apply the necessary domain constraint checks immediately. This fixes
cases where domain constraints went unchecked for statement parameters,
PL function local variables and results, etc. We can also eliminate existing
special cases for domains in places that had gotten it right, eg COPY.
Also, allow domains over domains (base of a domain is another domain type).
This almost worked before, but was disallowed because the original patch
hadn't gotten it quite right.
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/access/common/printtup.c | 10 | ||||
| -rw-r--r-- | src/backend/commands/copy.c | 67 | ||||
| -rw-r--r-- | src/backend/commands/typecmds.c | 28 | ||||
| -rw-r--r-- | src/backend/optimizer/prep/preptlist.c | 4 | ||||
| -rw-r--r-- | src/backend/parser/parse_coerce.c | 60 | ||||
| -rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 4 | ||||
| -rw-r--r-- | src/backend/rewrite/rewriteManip.c | 4 | ||||
| -rw-r--r-- | src/backend/utils/adt/Makefile | 5 | ||||
| -rw-r--r-- | src/backend/utils/adt/domains.c | 297 | ||||
| -rw-r--r-- | src/backend/utils/cache/lsyscache.c | 49 |
10 files changed, 379 insertions, 149 deletions
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index ba5793b0e7e..7eb46bb6ec5 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.95 2006/04/04 19:35:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.96 2006/04/05 22:11:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -177,7 +177,6 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) { Oid atttypid = attrs[i]->atttypid; int32 atttypmod = attrs[i]->atttypmod; - Oid basetype; pq_sendstring(&buf, NameStr(attrs[i]->attname)); /* column ID info appears in protocol 3.0 and up */ @@ -203,12 +202,7 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats) } } /* If column is a domain, send the base type and typmod instead */ - basetype = getBaseType(atttypid); - if (basetype != atttypid) - { - atttypmod = get_typtypmod(atttypid); - atttypid = basetype; - } + atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); pq_sendint(&buf, (int) atttypid, sizeof(atttypid)); pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen)); /* typmod appears in protocol 2.0 and up */ diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index af21d565f17..2b49094bea0 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.262 2006/04/04 19:35:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.263 2006/04/05 22:11:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1545,9 +1545,7 @@ CopyFrom(CopyState cstate) FmgrInfo oid_in_function; Oid *typioparams; Oid oid_typioparam; - ExprState **constraintexprs; bool *force_notnull; - bool hasConstraints = false; int attnum; int i; Oid in_func_oid; @@ -1608,7 +1606,6 @@ CopyFrom(CopyState cstate) typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid)); defmap = (int *) palloc(num_phys_attrs * sizeof(int)); defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *)); - constraintexprs = (ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *)); force_notnull = (bool *) palloc(num_phys_attrs * sizeof(bool)); for (attnum = 1; attnum <= num_phys_attrs; attnum++) @@ -1646,35 +1643,6 @@ CopyFrom(CopyState cstate) num_defaults++; } } - - /* If it's a domain type, set up to check domain constraints */ - if (get_typtype(attr[attnum - 1]->atttypid) == 'd') - { - Param *prm; - Node *node; - - /* - * Easiest way to do this is to use parse_coerce.c to set up an - * expression that checks the constraints. (At present, the - * expression might contain a length-coercion-function call and/or - * CoerceToDomain nodes.) The bottom of the expression is a Param - * node so that we can fill in the actual datum during the data - * input loop. - */ - prm = makeNode(Param); - prm->paramkind = PARAM_EXEC; - prm->paramid = 0; - prm->paramtype = getBaseType(attr[attnum - 1]->atttypid); - - node = coerce_to_domain((Node *) prm, - prm->paramtype, - attr[attnum - 1]->atttypid, - COERCE_IMPLICIT_CAST, false, false); - - constraintexprs[attnum - 1] = ExecPrepareExpr((Expr *) node, - estate); - hasConstraints = true; - } } /* Prepare to catch AFTER triggers. */ @@ -1743,11 +1711,6 @@ CopyFrom(CopyState cstate) nfields = file_has_oids ? (attr_count + 1) : attr_count; field_strings = (char **) palloc(nfields * sizeof(char *)); - /* Make room for a PARAM_EXEC value for domain constraint checks */ - if (hasConstraints) - econtext->ecxt_param_exec_vals = (ParamExecData *) - palloc0(sizeof(ParamExecData)); - /* Initialize state variables */ cstate->fe_eof = false; cstate->eol_type = EOL_UNKNOWN; @@ -1942,33 +1905,6 @@ CopyFrom(CopyState cstate) nulls[defmap[i]] = ' '; } - /* Next apply any domain constraints */ - if (hasConstraints) - { - ParamExecData *prmdata = &econtext->ecxt_param_exec_vals[0]; - - for (i = 0; i < num_phys_attrs; i++) - { - ExprState *exprstate = constraintexprs[i]; - - if (exprstate == NULL) - continue; /* no constraint for this attr */ - - /* Insert current row's value into the Param value */ - prmdata->value = values[i]; - prmdata->isnull = (nulls[i] == 'n'); - - /* - * Execute the constraint expression. Allow the expression to - * replace the value (consider e.g. a timestamp precision - * restriction). - */ - values[i] = ExecEvalExpr(exprstate, econtext, - &isnull, NULL); - nulls[i] = isnull ? 'n' : ' '; - } - } - /* And now we can form the input tuple. */ tuple = heap_formtuple(tupDesc, values, nulls); @@ -2043,7 +1979,6 @@ CopyFrom(CopyState cstate) pfree(typioparams); pfree(defmap); pfree(defexprs); - pfree(constraintexprs); pfree(force_notnull); ExecDropSingleTupleTableSlot(slot); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 83143496dbc..21546d1c8b0 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.89 2006/03/14 22:48:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.90 2006/04/05 22:11:55 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -536,6 +536,7 @@ DefineDomain(CreateDomainStmt *stmt) Oid sendProcedure; Oid analyzeProcedure; bool byValue; + Oid typelem; char delimiter; char alignment; char storage; @@ -547,7 +548,6 @@ DefineDomain(CreateDomainStmt *stmt) char *defaultValueBin = NULL; bool typNotNull = false; bool nullDefined = false; - Oid basetypelem; int32 typNDims = list_length(stmt->typename->arrayBounds); HeapTuple typeTup; List *schema = stmt->constraints; @@ -589,12 +589,12 @@ DefineDomain(CreateDomainStmt *stmt) basetypeoid = HeapTupleGetOid(typeTup); /* - * Base type must be a plain base type. Domains over pseudo types would - * create a security hole. Domains of domains might be made to work in - * the future, but not today. Ditto for domains over complex types. + * Base type must be a plain base type or another domain. Domains over + * pseudotypes would create a security hole. Domains over composite + * types might be made to work in the future, but not today. */ typtype = baseType->typtype; - if (typtype != 'b') + if (typtype != 'b' && typtype != 'd') ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("\"%s\" is not a valid base type for a domain", @@ -612,13 +612,16 @@ DefineDomain(CreateDomainStmt *stmt) /* Storage Length */ internalLength = baseType->typlen; + /* Array element type (in case base type is an array) */ + typelem = baseType->typelem; + /* Array element Delimiter */ delimiter = baseType->typdelim; /* I/O Functions */ - inputProcedure = baseType->typinput; + inputProcedure = F_DOMAIN_IN; outputProcedure = baseType->typoutput; - receiveProcedure = baseType->typreceive; + receiveProcedure = F_DOMAIN_RECV; sendProcedure = baseType->typsend; /* Analysis function */ @@ -637,13 +640,6 @@ DefineDomain(CreateDomainStmt *stmt) defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum)); /* - * Pull out the typelem name of the parent OID. - * - * This is what enables us to make a domain of an array - */ - basetypelem = baseType->typelem; - - /* * Run through constraints manually to avoid the additional processing * conducted by DefineRelation() and friends. */ @@ -776,7 +772,7 @@ DefineDomain(CreateDomainStmt *stmt) receiveProcedure, /* receive procedure */ sendProcedure, /* send procedure */ analyzeProcedure, /* analyze procedure */ - basetypelem, /* element type ID */ + typelem, /* element type ID */ basetypeoid, /* base type ID */ defaultValue, /* default type value (text) */ defaultValueBin, /* default type value (binary) */ diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 436d6fdce06..5a37b86eb5b 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.80 2006/03/05 15:58:31 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.81 2006/04/05 22:11:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -249,7 +249,7 @@ expand_targetlist(List *tlist, int command_type, true, /* isnull */ att_tup->attbyval); new_expr = coerce_to_domain(new_expr, - InvalidOid, + InvalidOid, -1, atttype, COERCE_IMPLICIT_CAST, false, diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 5a343e768dd..98393a54aa1 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.136 2006/04/04 19:35:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.137 2006/04/05 22:11:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -158,10 +158,24 @@ coerce_type(ParseState *pstate, Node *node, */ Const *con = (Const *) node; Const *newcon = makeNode(Const); - Type targetType = typeidType(targetTypeId); - char targetTyptype = typeTypType(targetType); + Oid baseTypeId; + int32 baseTypeMod; + Type targetType; - newcon->consttype = targetTypeId; + /* + * If the target type is a domain, we want to call its base type's + * input routine, not domain_in(). This is to avoid premature + * failure when the domain applies a typmod: existing input + * routines follow implicit-coercion semantics for length checks, + * which is not always what we want here. The needed check will + * be applied properly inside coerce_to_domain(). + */ + baseTypeMod = -1; + baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); + + targetType = typeidType(baseTypeId); + + newcon->consttype = baseTypeId; newcon->constlen = typeLen(targetType); newcon->constbyval = typeByVal(targetType); newcon->constisnull = con->constisnull; @@ -185,8 +199,10 @@ coerce_type(ParseState *pstate, Node *node, result = (Node *) newcon; /* If target is a domain, apply constraints. */ - if (targetTyptype == 'd') - result = coerce_to_domain(result, InvalidOid, targetTypeId, + if (baseTypeId != targetTypeId) + result = coerce_to_domain(result, + baseTypeId, baseTypeMod, + targetTypeId, cformat, false, false); ReleaseSysCache(targetType); @@ -239,9 +255,7 @@ coerce_type(ParseState *pstate, Node *node, param->paramtype = targetTypeId; - /* Apply domain constraints, if necessary */ - return coerce_to_domain((Node *) param, InvalidOid, targetTypeId, - cformat, false, false); + return (Node *) param; } if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId)) @@ -255,13 +269,11 @@ coerce_type(ParseState *pstate, Node *node, * and we need to extract the correct typmod to use from the * domain's typtypmod. */ - Oid baseTypeId = getBaseType(targetTypeId); + Oid baseTypeId; int32 baseTypeMod; - if (targetTypeId != baseTypeId) - baseTypeMod = get_typtypmod(targetTypeId); - else - baseTypeMod = targetTypeMod; + baseTypeMod = targetTypeMod; + baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); result = build_coercion_expression(node, funcId, baseTypeId, baseTypeMod, @@ -274,7 +286,8 @@ coerce_type(ParseState *pstate, Node *node, * selected coercion function was a type-and-length coercion. */ if (targetTypeId != baseTypeId) - result = coerce_to_domain(result, baseTypeId, targetTypeId, + result = coerce_to_domain(result, baseTypeId, baseTypeMod, + targetTypeId, cformat, true, exprIsLengthCoercion(result, NULL)); @@ -290,7 +303,7 @@ coerce_type(ParseState *pstate, Node *node, * that must be accounted for. If the destination is a domain * then we won't need a RelabelType node. */ - result = coerce_to_domain(node, InvalidOid, targetTypeId, + result = coerce_to_domain(node, InvalidOid, -1, targetTypeId, cformat, false, false); if (result == node) { @@ -439,15 +452,18 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, * 'arg': input expression * 'baseTypeId': base type of domain, if known (pass InvalidOid if caller * has not bothered to look this up) + * 'baseTypeMod': base type typmod of domain, if known (pass -1 if caller + * has not bothered to look this up) * 'typeId': target type to coerce to * 'cformat': coercion format * 'hideInputCoercion': if true, hide the input coercion under this one. - * 'lengthCoercionDone': if true, caller already accounted for length. + * 'lengthCoercionDone': if true, caller already accounted for length, + * ie the input is already of baseTypMod as well as baseTypeId. * * If the target type isn't a domain, the given 'arg' is returned as-is. */ Node * -coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, +coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, CoercionForm cformat, bool hideInputCoercion, bool lengthCoercionDone) { @@ -455,7 +471,7 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, /* Get the base type if it hasn't been supplied */ if (baseTypeId == InvalidOid) - baseTypeId = getBaseType(typeId); + baseTypeId = getBaseTypeAndTypmod(typeId, &baseTypeMod); /* If it isn't a domain, return the node as it was passed in */ if (baseTypeId == typeId) @@ -480,10 +496,8 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, */ if (!lengthCoercionDone) { - int32 typmod = get_typtypmod(typeId); - - if (typmod >= 0) - arg = coerce_type_typmod(arg, baseTypeId, typmod, + if (baseTypeMod >= 0) + arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod, COERCE_IMPLICIT_CAST, (cformat != COERCE_IMPLICIT_CAST), false); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index acebefca3f0..6d1ace66f1a 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.161 2006/03/05 15:58:36 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.162 2006/04/05 22:11:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -599,7 +599,7 @@ rewriteTargetList(Query *parsetree, Relation target_relation) att_tup->attbyval); /* this is to catch a NOT NULL domain constraint */ new_expr = coerce_to_domain(new_expr, - InvalidOid, + InvalidOid, -1, att_tup->atttypid, COERCE_IMPLICIT_CAST, false, diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index d7cd8de6774..175df7a695a 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.97 2006/03/05 15:58:36 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.98 2006/04/05 22:11:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -860,7 +860,7 @@ resolve_one_var(Var *var, ResolveNew_context *context) /* Otherwise replace unmatched var with a null */ /* need coerce_to_domain in case of NOT NULL domain constraint */ return coerce_to_domain((Node *) makeNullConst(var->vartype), - InvalidOid, + InvalidOid, -1, var->vartype, COERCE_IMPLICIT_CAST, false, diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 294fd9de49b..5a1996c3439 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -1,7 +1,7 @@ # # Makefile for utils/adt # -# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.59 2005/08/12 03:24:08 momjian Exp $ +# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.60 2006/04/05 22:11:55 tgl Exp $ # subdir = src/backend/utils/adt @@ -16,7 +16,8 @@ endif endif OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \ - cash.o char.o date.o datetime.o datum.o float.o format_type.o \ + cash.o char.o date.o datetime.o datum.o domains.o \ + float.o format_type.o \ geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \ misc.o nabstime.o name.o not_in.o numeric.o numutils.o \ oid.o oracle_compat.o pseudotypes.o rowtypes.o \ diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c new file mode 100644 index 00000000000..051145f2376 --- /dev/null +++ b/src/backend/utils/adt/domains.c @@ -0,0 +1,297 @@ +/*------------------------------------------------------------------------- + * + * domains.c + * I/O functions for domain types. + * + * The output functions for a domain type are just the same ones provided + * by its underlying base type. The input functions, however, must be + * prepared to apply any constraints defined by the type. So, we create + * special input functions that invoke the base type's input function + * and then check the constraints. + * + * The overhead required for constraint checking can be high, since examining + * the catalogs to discover the constraints for a given domain is not cheap. + * We have two mechanisms for minimizing this cost: + * 1. In a nest of domains, we flatten the checking of all the levels + * into just one operation. + * 2. We cache the list of constraint items in the FmgrInfo struct + * passed by the caller. + * + * We also have to create an EState to evaluate CHECK expressions in. + * Creating and destroying an EState is somewhat expensive, and so it's + * tempting to cache the EState too. However, that would mean that the + * EState never gets an explicit FreeExecutorState call, which is a bad idea + * because it risks leaking non-memory resources. + * + * + * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.1 2006/04/05 22:11:55 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "commands/typecmds.h" +#include "executor/executor.h" +#include "lib/stringinfo.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" + + +/* + * structure to cache state across multiple calls + */ +typedef struct DomainIOData +{ + Oid domain_type; + /* Data needed to call base type's input function */ + Oid typiofunc; + Oid typioparam; + int32 typtypmod; + FmgrInfo proc; + /* List of constraint items to check */ + List *constraint_list; +} DomainIOData; + + +/* + * domain_state_setup - initialize the cache for a new domain type. + */ +static void +domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary, + MemoryContext mcxt) +{ + Oid baseType; + MemoryContext oldcontext; + + /* Mark cache invalid */ + my_extra->domain_type = InvalidOid; + + /* Find out the base type */ + my_extra->typtypmod = -1; + baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod); + if (baseType == domainType) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type %s is not a domain", + format_type_be(domainType)))); + + /* Look up underlying I/O function */ + if (binary) + getTypeBinaryInputInfo(baseType, + &my_extra->typiofunc, + &my_extra->typioparam); + else + getTypeInputInfo(baseType, + &my_extra->typiofunc, + &my_extra->typioparam); + fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt); + + /* Look up constraints for domain */ + oldcontext = MemoryContextSwitchTo(mcxt); + my_extra->constraint_list = GetDomainConstraints(domainType); + MemoryContextSwitchTo(oldcontext); + + /* Mark cache valid */ + my_extra->domain_type = domainType; +} + +/* + * domain_check_input - apply the cached checks. + * + * This is extremely similar to ExecEvalCoerceToDomain in execQual.c. + */ +static void +domain_check_input(Datum value, bool isnull, DomainIOData *my_extra) +{ + EState *estate = NULL; + ListCell *l; + + foreach(l, my_extra->constraint_list) + { + DomainConstraintState *con = (DomainConstraintState *) lfirst(l); + + switch (con->constrainttype) + { + case DOM_CONSTRAINT_NOTNULL: + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_NOT_NULL_VIOLATION), + errmsg("domain %s does not allow null values", + format_type_be(my_extra->domain_type)))); + break; + case DOM_CONSTRAINT_CHECK: + { + ExprContext *econtext; + Datum conResult; + bool conIsNull; + Datum save_datum; + bool save_isNull; + + if (estate == NULL) + estate = CreateExecutorState(); + econtext = GetPerTupleExprContext(estate); + + /* + * Set up value to be returned by CoerceToDomainValue + * nodes. We must save and restore prior setting of + * econtext's domainValue fields, in case this node is + * itself within a check expression for another domain. + */ + save_datum = econtext->domainValue_datum; + save_isNull = econtext->domainValue_isNull; + + econtext->domainValue_datum = value; + econtext->domainValue_isNull = isnull; + + conResult = ExecEvalExprSwitchContext(con->check_expr, + econtext, + &conIsNull, NULL); + + if (!conIsNull && + !DatumGetBool(conResult)) + ereport(ERROR, + (errcode(ERRCODE_CHECK_VIOLATION), + errmsg("value for domain %s violates check constraint \"%s\"", + format_type_be(my_extra->domain_type), + con->name))); + econtext->domainValue_datum = save_datum; + econtext->domainValue_isNull = save_isNull; + + break; + } + default: + elog(ERROR, "unrecognized constraint type: %d", + (int) con->constrainttype); + break; + } + } + + if (estate) + FreeExecutorState(estate); +} + + +/* + * domain_in - input routine for any domain type. + */ +Datum +domain_in(PG_FUNCTION_ARGS) +{ + char *string; + Oid domainType; + DomainIOData *my_extra; + Datum value; + + /* + * Since domain_in is not strict, we have to check for null inputs. + * The typioparam argument should never be null in normal system usage, + * but it could be null in a manual invocation --- if so, just return null. + */ + if (PG_ARGISNULL(0)) + string = NULL; + else + string = PG_GETARG_CSTRING(0); + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); + domainType = PG_GETARG_OID(1); + + /* + * We arrange to look up the needed info just once per series of + * calls, assuming the domain type doesn't change underneath us. + */ + my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(DomainIOData)); + domain_state_setup(my_extra, domainType, false, + fcinfo->flinfo->fn_mcxt); + fcinfo->flinfo->fn_extra = (void *) my_extra; + } + else if (my_extra->domain_type != domainType) + domain_state_setup(my_extra, domainType, false, + fcinfo->flinfo->fn_mcxt); + + /* + * Invoke the base type's typinput procedure to convert the data. + */ + value = InputFunctionCall(&my_extra->proc, + string, + my_extra->typioparam, + my_extra->typtypmod); + + /* + * Do the necessary checks to ensure it's a valid domain value. + */ + domain_check_input(value, (string == NULL), my_extra); + + if (string == NULL) + PG_RETURN_NULL(); + else + PG_RETURN_DATUM(value); +} + +/* + * domain_recv - binary input routine for any domain type. + */ +Datum +domain_recv(PG_FUNCTION_ARGS) +{ + StringInfo buf; + Oid domainType; + DomainIOData *my_extra; + Datum value; + + /* + * Since domain_recv is not strict, we have to check for null inputs. + * The typioparam argument should never be null in normal system usage, + * but it could be null in a manual invocation --- if so, just return null. + */ + if (PG_ARGISNULL(0)) + buf = NULL; + else + buf = (StringInfo) PG_GETARG_POINTER(0); + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); + domainType = PG_GETARG_OID(1); + + /* + * We arrange to look up the needed info just once per series of + * calls, assuming the domain type doesn't change underneath us. + */ + my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(DomainIOData)); + domain_state_setup(my_extra, domainType, true, + fcinfo->flinfo->fn_mcxt); + fcinfo->flinfo->fn_extra = (void *) my_extra; + } + else if (my_extra->domain_type != domainType) + domain_state_setup(my_extra, domainType, true, + fcinfo->flinfo->fn_mcxt); + + /* + * Invoke the base type's typreceive procedure to convert the data. + */ + value = ReceiveFunctionCall(&my_extra->proc, + buf, + my_extra->typioparam, + my_extra->typtypmod); + + /* + * Do the necessary checks to ensure it's a valid domain value. + */ + domain_check_input(value, (buf == NULL), my_extra); + + if (buf == NULL) + PG_RETURN_NULL(); + else + PG_RETURN_DATUM(value); +} diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index fb24cc62366..a52366c5347 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.133 2006/04/04 19:35:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.134 2006/04/05 22:11:55 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -1470,33 +1470,6 @@ get_typstorage(Oid typid) } /* - * get_typtypmod - * - * Given the type OID, return the typtypmod field (domain's typmod - * for base type) - */ -int32 -get_typtypmod(Oid typid) -{ - HeapTuple tp; - - tp = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - int32 result; - - result = typtup->typtypmod; - ReleaseSysCache(tp); - return result; - } - else - return -1; -} - -/* * get_typdefault * Given a type OID, return the type's default value, if any. * @@ -1584,6 +1557,23 @@ get_typdefault(Oid typid) Oid getBaseType(Oid typid) { + int32 typmod = -1; + + return getBaseTypeAndTypmod(typid, &typmod); +} + +/* + * getBaseTypeAndTypmod + * If the given type is a domain, return its base type and typmod; + * otherwise return the type's own OID, and leave *typmod unchanged. + * + * Note that the "applied typmod" should be -1 for every domain level + * above the bottommost; therefore, if the passed-in typid is indeed + * a domain, *typmod should be -1. + */ +Oid +getBaseTypeAndTypmod(Oid typid, int32 *typmod) +{ /* * We loop to find the bottom base type in a stack of domains. */ @@ -1605,7 +1595,10 @@ getBaseType(Oid typid) break; } + Assert(*typmod == -1); typid = typTup->typbasetype; + *typmod = typTup->typtypmod; + ReleaseSysCache(tup); } |
