diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 4beb0ae53b..bfed902739 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -1,5 +1,5 @@
@@ -35,8 +35,10 @@ where constraint is:
Description
- CREATE DOMAIN creates a new data domain. The
- user who defines a domain becomes its owner.
+ CREATE DOMAIN creates a new domain. A domain is
+ essentially a data type with optional constraints (restrictions on
+ the allowed set of values).
+ The user who defines a domain becomes its owner.
@@ -48,24 +50,13 @@ where constraint is:
- Domains are useful for abstracting common fields between tables
- into a single location for maintenance. For example, an email address
- column may be used in several tables, all with the same properties.
- Define a domain and use that rather than setting up each table's
- constraints individually.
+ Domains are useful for abstracting common constraints on fields into
+ a single location for maintenance. For example, several tables might
+ contain email address columns, all requiring the same CHECK constraint
+ to verify the address syntax.
+ Define a domain rather than setting up each table's constraint
+ individually.
-
-
-
- At present, declaring a function result value as a domain
- is pretty dangerous, because none of the procedural languages enforce domain constraints
- on their results. You'll need to make sure that the function code itself
- respects the constraints. In PL/pgSQL>, one possible
- workaround is to explicitly cast the result value to the domain type
- when you return it. PL/pgSQL> does not enforce domain
- constraints for local variables within functions, either.
-
-
@@ -156,7 +147,7 @@ where constraint is:
CHECK> clauses specify integrity constraints or tests
which values of the domain must satisfy.
Each constraint must be an expression
- producing a Boolean result. It should use the name VALUE>
+ producing a Boolean result. It should use the key word VALUE>
to refer to the value being tested.
@@ -185,12 +176,12 @@ OR VALUE ~ '^\\d{5}-\\d{4}$'
);
CREATE TABLE us_snail_addy (
- address_id SERIAL PRIMARY KEY
-, street1 TEXT NOT NULL
-, street2 TEXT
-, street3 TEXT
-, city TEXT NOT NULL
-, postal us_postal_code NOT NULL
+ address_id SERIAL PRIMARY KEY,
+ street1 TEXT NOT NULL,
+ street2 TEXT,
+ street3 TEXT,
+ city TEXT NOT NULL,
+ postal us_postal_code NOT NULL
);
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index e3b8b44d8f..68ec242ae5 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -1,5 +1,5 @@
@@ -591,6 +591,7 @@ CREATE TABLE big_objs (
+
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index ba5793b0e7..7eb46bb6ec 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 af21d565f1..2b49094bea 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 83143496db..21546d1c8b 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 */
@@ -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) */
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 436d6fdce0..5a37b86eb5 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 5a343e768d..98393a54aa 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 acebefca3f..6d1ace66f1 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 d7cd8de677..175df7a695 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 294fd9de49..5a1996c343 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 0000000000..051145f237
--- /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 fb24cc6236..a52366c534 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.
@@ -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);
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2ae52c723f..6b59dd55fe 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.321 2006/03/16 00:31:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.322 2006/04/05 22:11:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200603151
+#define CATALOG_VERSION_NO 200604051
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d8fda0b8c3..9542d632f2 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.404 2006/03/10 20:15:26 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.405 2006/04/05 22:11:55 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -3363,6 +3363,10 @@ DATA(insert OID = 2398 ( shell_in PGNSP PGUID 12 f f t f i 1 2282 "2275" _nul
DESCR("I/O");
DATA(insert OID = 2399 ( shell_out PGNSP PGUID 12 f f t f i 1 2275 "2282" _null_ _null_ _null_ shell_out - _null_ ));
DESCR("I/O");
+DATA(insert OID = 2597 ( domain_in PGNSP PGUID 12 f f f f v 3 2276 "2275 26 23" _null_ _null_ _null_ domain_in - _null_ ));
+DESCR("I/O");
+DATA(insert OID = 2598 ( domain_recv PGNSP PGUID 12 f f f f v 3 2276 "2281 26 23" _null_ _null_ _null_ domain_recv - _null_ ));
+DESCR("I/O");
/* cryptographic */
DATA(insert OID = 2311 ( md5 PGNSP PGUID 12 f f t f i 1 25 "25" _null_ _null_ _null_ md5_text - _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 00b7bf3dc3..b9aeec1e3c 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.170 2006/03/05 15:58:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.171 2006/04/05 22:11:57 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -156,7 +156,7 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP
bool typnotnull;
/*
- * Domains use typbasetype to show the base (or complex) type that the
+ * Domains use typbasetype to show the base (or domain) type that the
* domain is based on. Zero if the type is not a domain.
*/
Oid typbasetype;
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 871c82892d..70b7d07489 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.61 2006/03/05 15:58:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.62 2006/04/05 22:11:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,7 +49,8 @@ extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
extern Node *coerce_type(ParseState *pstate, Node *node,
Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat);
-extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
+extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
+ Oid typeId,
CoercionForm cformat, bool hideInputCoercion,
bool lengthCoercionDone);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 3c48416974..47553c5cf2 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.277 2006/03/10 20:15:27 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.278 2006/04/05 22:11:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -99,6 +99,10 @@ extern Datum i4tochar(PG_FUNCTION_ARGS);
extern Datum text_char(PG_FUNCTION_ARGS);
extern Datum char_text(PG_FUNCTION_ARGS);
+/* domains.c */
+extern Datum domain_in(PG_FUNCTION_ARGS);
+extern Datum domain_recv(PG_FUNCTION_ARGS);
+
/* int.c */
extern Datum int2in(PG_FUNCTION_ARGS);
extern Datum int2out(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 6e0d0de2d6..9a33fc3602 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.103 2006/03/05 15:59:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.104 2006/04/05 22:11:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -86,7 +86,6 @@ extern void get_type_io_data(Oid typid,
Oid *typioparam,
Oid *func);
extern char get_typstorage(Oid typid);
-extern int32 get_typtypmod(Oid typid);
extern Node *get_typdefault(Oid typid);
extern char get_typtype(Oid typid);
extern Oid get_typ_typrelid(Oid typid);
@@ -97,6 +96,7 @@ extern void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena);
extern void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam);
extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena);
extern Oid getBaseType(Oid typid);
+extern Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod);
extern int32 get_typavgwidth(Oid typid, int32 typmod);
extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
extern bool get_attstatsslot(HeapTuple statstuple,
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index c89be9535f..86e2ca54bf 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -1,10 +1,17 @@
+--
+-- Test domains.
+--
-- Test Comment / Drop
create domain domaindroptest int4;
comment on domain domaindroptest is 'About to drop this..';
--- currently this will be disallowed
-create domain basetypetest domaindroptest;
-ERROR: "domaindroptest" is not a valid base type for a domain
+create domain dependenttypetest domaindroptest;
+-- fail because of dependent type
drop domain domaindroptest;
+NOTICE: type dependenttypetest depends on type domaindroptest
+ERROR: cannot drop type domaindroptest because other objects depend on it
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+drop domain domaindroptest cascade;
+NOTICE: drop cascades to type dependenttypetest
-- this should fail because already gone
drop domain domaindroptest cascade;
ERROR: type "domaindroptest" does not exist
@@ -40,7 +47,7 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate
-- Test copy
COPY basictest (testvarchar) FROM stdin; -- fail
ERROR: value too long for type character varying(5)
-CONTEXT: COPY basictest, line 1: "notsoshorttext"
+CONTEXT: COPY basictest, line 1, column testvarchar: "notsoshorttext"
COPY basictest (testvarchar) FROM stdin;
select * from basictest;
testint4 | testtext | testvarchar | testnumeric
@@ -126,8 +133,11 @@ ERROR: null value in column "col3" violates not-null constraint
INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
+ERROR: null value in column "col3" violates not-null constraint
+CONTEXT: COPY nulltest, line 1: "a b \N d d"
+COPY nulltest FROM stdin; --fail
ERROR: domain dcheck does not allow null values
-CONTEXT: COPY nulltest, line 1: "a b \N d \N"
+CONTEXT: COPY nulltest, line 1, column col5: NULL input
-- Last row is bad
COPY nulltest FROM stdin;
ERROR: new row for relation "nulltest" violates check constraint "nulltest_col5_check"
@@ -300,6 +310,46 @@ drop domain ddef3 restrict;
drop domain ddef4 restrict;
drop domain ddef5 restrict;
drop sequence ddef4_seq;
+-- Test domains over domains
+create domain vchar4 varchar(4);
+create domain dinter vchar4 check (substring(VALUE, 1, 1) = 'x');
+create domain dtop dinter check (substring(VALUE, 2, 1) = '1');
+select 'x123'::dtop;
+ dtop
+------
+ x123
+(1 row)
+
+select 'x1234'::dtop; -- explicit coercion should truncate
+ dtop
+------
+ x123
+(1 row)
+
+select 'y1234'::dtop; -- fail
+ERROR: value for domain dtop violates check constraint "dinter_check"
+select 'y123'::dtop; -- fail
+ERROR: value for domain dtop violates check constraint "dinter_check"
+select 'yz23'::dtop; -- fail
+ERROR: value for domain dtop violates check constraint "dinter_check"
+select 'xz23'::dtop; -- fail
+ERROR: value for domain dtop violates check constraint "dtop_check"
+create temp table dtest(f1 dtop);
+insert into dtest values('x123');
+insert into dtest values('x1234'); -- fail, implicit coercion
+ERROR: value too long for type character varying(4)
+insert into dtest values('y1234'); -- fail, implicit coercion
+ERROR: value too long for type character varying(4)
+insert into dtest values('y123'); -- fail
+ERROR: value for domain dtop violates check constraint "dinter_check"
+insert into dtest values('yz23'); -- fail
+ERROR: value for domain dtop violates check constraint "dinter_check"
+insert into dtest values('xz23'); -- fail
+ERROR: value for domain dtop violates check constraint "dtop_check"
+drop table dtest;
+drop domain vchar4 cascade;
+NOTICE: drop cascades to type dinter
+NOTICE: drop cascades to type dtop
-- Make sure that constraints of newly-added domain columns are
-- enforced correctly, even if there's no default value for the new
-- column. Per bug #1433
@@ -328,3 +378,27 @@ execute s1(0); -- should fail
ERROR: value for domain pos_int violates check constraint "pos_int_check"
execute s1(NULL); -- should fail
ERROR: domain pos_int does not allow null values
+-- Check that domain constraints on plpgsql function parameters, results,
+-- and local variables are enforced correctly.
+create function doubledecrement(p1 pos_int) returns pos_int as $$
+declare v pos_int;
+begin
+ v := p1 - 1;
+ return v - 1;
+end$$ language plpgsql;
+select doubledecrement(null); -- fail before call
+ERROR: domain pos_int does not allow null values
+select doubledecrement(0); -- fail before call
+ERROR: value for domain pos_int violates check constraint "pos_int_check"
+select doubledecrement(1); -- fail at assignment to v
+ERROR: value for domain pos_int violates check constraint "pos_int_check"
+CONTEXT: PL/pgSQL function "doubledecrement" line 3 at assignment
+select doubledecrement(2); -- fail at return
+ERROR: value for domain pos_int violates check constraint "pos_int_check"
+CONTEXT: PL/pgSQL function "doubledecrement" while casting return value to function's return type
+select doubledecrement(3); -- good
+ doubledecrement
+-----------------
+ 1
+(1 row)
+
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index f6010e636c..21940e0e61 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -1,14 +1,18 @@
-
+--
+-- Test domains.
+--
-- Test Comment / Drop
create domain domaindroptest int4;
comment on domain domaindroptest is 'About to drop this..';
--- currently this will be disallowed
-create domain basetypetest domaindroptest;
+create domain dependenttypetest domaindroptest;
+-- fail because of dependent type
drop domain domaindroptest;
+drop domain domaindroptest cascade;
+
-- this should fail because already gone
drop domain domaindroptest cascade;
@@ -101,7 +105,11 @@ INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
-a b \N d \N
+a b \N d d
+\.
+
+COPY nulltest FROM stdin; --fail
+a b c d \N
\.
-- Last row is bad
@@ -245,6 +253,30 @@ drop domain ddef4 restrict;
drop domain ddef5 restrict;
drop sequence ddef4_seq;
+-- Test domains over domains
+create domain vchar4 varchar(4);
+create domain dinter vchar4 check (substring(VALUE, 1, 1) = 'x');
+create domain dtop dinter check (substring(VALUE, 2, 1) = '1');
+
+select 'x123'::dtop;
+select 'x1234'::dtop; -- explicit coercion should truncate
+select 'y1234'::dtop; -- fail
+select 'y123'::dtop; -- fail
+select 'yz23'::dtop; -- fail
+select 'xz23'::dtop; -- fail
+
+create temp table dtest(f1 dtop);
+
+insert into dtest values('x123');
+insert into dtest values('x1234'); -- fail, implicit coercion
+insert into dtest values('y1234'); -- fail, implicit coercion
+insert into dtest values('y123'); -- fail
+insert into dtest values('yz23'); -- fail
+insert into dtest values('xz23'); -- fail
+
+drop table dtest;
+drop domain vchar4 cascade;
+
-- Make sure that constraints of newly-added domain columns are
-- enforced correctly, even if there's no default value for the new
-- column. Per bug #1433
@@ -271,3 +303,19 @@ prepare s1 as select $1::pos_int = 10 as "is_ten";
execute s1(10);
execute s1(0); -- should fail
execute s1(NULL); -- should fail
+
+-- Check that domain constraints on plpgsql function parameters, results,
+-- and local variables are enforced correctly.
+
+create function doubledecrement(p1 pos_int) returns pos_int as $$
+declare v pos_int;
+begin
+ v := p1 - 1;
+ return v - 1;
+end$$ language plpgsql;
+
+select doubledecrement(null); -- fail before call
+select doubledecrement(0); -- fail before call
+select doubledecrement(1); -- fail at assignment to v
+select doubledecrement(2); -- fail at return
+select doubledecrement(3); -- good