1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

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.
This commit is contained in:
Tom Lane
2006-04-05 22:11:58 +00:00
parent 89a67e523e
commit 7fdb4305db
20 changed files with 549 additions and 196 deletions

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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 */
@@ -636,13 +639,6 @@ DefineDomain(CreateDomainStmt *stmt)
if (!isnull)
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) */

View File

@@ -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,

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 \

View File

@@ -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);
}

View File

@@ -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.
@@ -1469,33 +1469,6 @@ get_typstorage(Oid typid)
return 'p';
}
/*
* 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.
@@ -1583,6 +1556,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);
}