summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruce Momjian2002-07-06 20:16:36 +0000
committerBruce Momjian2002-07-06 20:16:36 +0000
commit1666970275cf6cc44d2944888a6199c31b3e6832 (patch)
treee79f2615f20b5c65600f0b95787e246a1f580996 /src
parent5af6e0a4ac57f11d071d6200f0264bf6798b64f6 (diff)
I've fixed up the way domain constraints (not null and type length)
are managed as per request. Moved from merging with table attributes to applying themselves during coerce_type() and coerce_type_typmod. Regression tests altered to test the cast() scenarios. Rod Taylor
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/tablecmds.c53
-rw-r--r--src/backend/executor/execQual.c47
-rw-r--r--src/backend/optimizer/util/clauses.c18
-rw-r--r--src/backend/parser/gram.y28
-rw-r--r--src/backend/parser/parse_coerce.c109
-rw-r--r--src/backend/parser/parse_expr.c9
-rw-r--r--src/backend/parser/parse_type.c26
-rw-r--r--src/backend/utils/cache/lsyscache.c39
-rw-r--r--src/include/utils/lsyscache.h3
-rw-r--r--src/test/regress/expected/domain.out31
-rw-r--r--src/test/regress/sql/domain.sql12
11 files changed, 263 insertions, 112 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b92bf4bba16..901090c70ac 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.18 2002/07/01 15:27:46 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.19 2002/07/06 20:16:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -48,7 +48,6 @@
#include "utils/relcache.h"
-static List *MergeDomainAttributes(List *schema);
static List *MergeAttributes(List *schema, List *supers, bool istemp,
List **supOids, List **supconstr, bool *supHasOids);
static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
@@ -123,13 +122,6 @@ DefineRelation(CreateStmt *stmt, char relkind)
}
/*
- * Merge domain attributes into the known columns before processing table
- * inheritance. Otherwise we risk adding double constraints to a
- * domain-type column that's inherited.
- */
- schema = MergeDomainAttributes(schema);
-
- /*
* Look up inheritance ancestors and generate relation schema,
* including inherited attributes.
*/
@@ -328,49 +320,6 @@ TruncateRelation(const RangeVar *relation)
heap_truncate(relid);
}
-
-/*
- * MergeDomainAttributes
- * Returns a new table schema with the constraints, types, and other
- * attributes of domains resolved for fields using a domain as
- * their type.
- */
-static List *
-MergeDomainAttributes(List *schema)
-{
- List *entry;
-
- /*
- * Loop through the table elements supplied. These should
- * never include inherited domains else they'll be
- * double (or more) processed.
- */
- foreach(entry, schema)
- {
- ColumnDef *coldef = lfirst(entry);
- HeapTuple tuple;
- Form_pg_type typeTup;
-
- tuple = typenameType(coldef->typename);
- typeTup = (Form_pg_type) GETSTRUCT(tuple);
-
- if (typeTup->typtype == 'd')
- {
- /* Force the column to have the correct typmod. */
- coldef->typename->typmod = typeTup->typtypmod;
- /* XXX more to do here? */
- }
-
- /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
- /* Currently only used for domains, but could be valid for all */
- coldef->is_not_null |= typeTup->typnotnull;
-
- ReleaseSysCache(tuple);
- }
-
- return schema;
-}
-
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 789251b70e7..6bb2b5fa942 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.96 2002/07/04 16:44:08 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.97 2002/07/06 20:16:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -66,6 +66,8 @@ static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone);
/*----------
@@ -1226,6 +1228,43 @@ ExecEvalNullTest(NullTest *ntest,
}
}
+/*
+ * ExecEvalConstraint
+ *
+ * Test the constraint against the data provided. If the data fits
+ * within the constraint specifications, pass it through (return the
+ * datum) otherwise throw an error.
+ */
+static Datum
+ExecEvalConstraint(Constraint *constraint, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
+{
+ Datum result;
+
+ result = ExecEvalExpr(constraint->raw_expr, econtext, isNull, isDone);
+
+ /* Test for the constraint type */
+ switch(constraint->contype)
+ {
+ case CONSTR_NOTNULL:
+ if (*isNull)
+ {
+ elog(ERROR, "Domain %s does not allow NULL values", constraint->name);
+ }
+ break;
+ case CONSTR_CHECK:
+
+ elog(ERROR, "ExecEvalConstraint: Domain CHECK Constraints not yet implemented");
+ break;
+ default:
+ elog(ERROR, "ExecEvalConstraint: Constraint type unknown");
+ break;
+ }
+
+ /* If all has gone well (constraint did not fail) return the datum */
+ return result;
+}
+
/* ----------------------------------------------------------------
* ExecEvalBooleanTest
*
@@ -1473,6 +1512,12 @@ ExecEvalExpr(Node *expression,
isNull,
isDone);
break;
+ case T_Constraint:
+ retDatum = ExecEvalConstraint((Constraint *) expression,
+ econtext,
+ isNull,
+ isDone);
+ break;
case T_CaseExpr:
retDatum = ExecEvalCase((CaseExpr *) expression,
econtext,
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 5b0ae52f7c6..889a10b9ee3 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.102 2002/07/04 15:23:58 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.103 2002/07/06 20:16:35 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1882,6 +1882,8 @@ expression_tree_walker(Node *node,
return true;
}
break;
+ case T_Constraint:
+ return walker(((Constraint *) node)->raw_expr, context);
case T_NullTest:
return walker(((NullTest *) node)->arg, context);
case T_BooleanTest:
@@ -2236,6 +2238,20 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_Constraint:
+ {
+ /*
+ * Used for confirming domains. Only needed fields
+ * within the executor are the name, raw expression
+ * and constraint type.
+ */
+ Constraint *con = (Constraint *) node;
+ Constraint *newnode;
+
+ FLATCOPY(newnode, con, Constraint);
+ MUTATE(newnode->raw_expr, con->raw_expr, Node *);
+ return (Node *) newnode;
+ }
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 410caf72e0a..e8e5aeabc77 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.336 2002/07/04 15:23:59 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.337 2002/07/06 20:16:35 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -6968,24 +6968,16 @@ static Node *
makeTypeCast(Node *arg, TypeName *typename)
{
/*
- * If arg is an A_Const, just stick the typename into the
- * field reserved for it --- unless there's something there already!
- * (We don't want to collapse x::type1::type2 into just x::type2.)
- * Otherwise, generate a TypeCast node.
+ * Simply generate a TypeCast node.
+ *
+ * Earlier we would determine whether an A_Const would
+ * be acceptable, however Domains require coerce_type()
+ * to process them -- applying constraints as required.
*/
- if (IsA(arg, A_Const) &&
- ((A_Const *) arg)->typename == NULL)
- {
- ((A_Const *) arg)->typename = typename;
- return arg;
- }
- else
- {
- TypeCast *n = makeNode(TypeCast);
- n->arg = arg;
- n->typename = typename;
- return (Node *) n;
- }
+ TypeCast *n = makeNode(TypeCast);
+ n->arg = arg;
+ n->typename = typename;
+ return (Node *) n;
}
static Node *
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 3c441509b1d..b258368626a 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.74 2002/06/20 20:29:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.75 2002/07/06 20:16:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -33,7 +33,7 @@ static Oid PreferredType(CATEGORY category, Oid type);
static Node *build_func_call(Oid funcid, Oid rettype, List *args);
static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
Oid secondArgType, bool isExplicit);
-
+static Node *TypeConstraints(Node *arg, Oid typeId);
/* coerce_type()
* Convert a function argument to a different type.
@@ -48,7 +48,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
targetTypeId == InvalidOid ||
node == NULL)
{
- /* no conversion needed */
+ /* no conversion needed, but constraints may need to be applied */
result = node;
}
else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
@@ -72,6 +72,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
Const *con = (Const *) node;
Const *newcon = makeNode(Const);
Type targetType = typeidType(targetTypeId);
+ Oid baseTypeId = getBaseType(targetTypeId);
newcon->consttype = targetTypeId;
newcon->constlen = typeLen(targetType);
@@ -83,14 +84,16 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
{
char *val = DatumGetCString(DirectFunctionCall1(unknownout,
con->constvalue));
-
newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
pfree(val);
}
ReleaseSysCache(targetType);
+ /* Test for domain, and apply appropriate constraints */
result = (Node *) newcon;
+ if (targetTypeId != baseTypeId)
+ result = (Node *) TypeConstraints(result, targetTypeId);
}
else if (IsBinaryCompatible(inputTypeId, targetTypeId))
{
@@ -103,8 +106,17 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
* default -1 typmod, to save a possible length-coercion later?
* Would work if both types have same interpretation of typmod,
* which is likely but not certain.
+ *
+ * Domains may have value restrictions beyond the base type that
+ * must be accounted for.
*/
- result = (Node *) makeRelabelType(node, targetTypeId, -1);
+ Oid baseTypeId = getBaseType(targetTypeId);
+ result = node;
+ if (targetTypeId != baseTypeId)
+ result = (Node *) TypeConstraints(result, targetTypeId);
+
+ result = (Node *) makeRelabelType(result, targetTypeId, -1);
+
}
else if (typeInheritsFrom(inputTypeId, targetTypeId))
{
@@ -125,8 +137,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
*
* For domains, we use the coercion function for the base type.
*/
- Oid baseTypeId = getBaseType(targetTypeId);
Oid funcId;
+ Oid baseTypeId = getBaseType(targetTypeId);
funcId = find_coercion_function(baseTypeId,
getBaseType(inputTypeId),
@@ -138,9 +150,12 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
result = build_func_call(funcId, baseTypeId, makeList1(node));
- /* if domain, relabel with domain type ID */
+ /*
+ * If domain, relabel with domain type ID and test against domain
+ * constraints
+ */
if (targetTypeId != baseTypeId)
- result = (Node *) makeRelabelType(result, targetTypeId, -1);
+ result = (Node *) TypeConstraints(result, targetTypeId);
/*
* If the input is a constant, apply the type conversion function
@@ -275,6 +290,21 @@ coerce_type_typmod(ParseState *pstate, Node *node,
{
Oid baseTypeId;
Oid funcId;
+ int32 domainTypMod = NULL;
+
+ /* If given type is a domain, use base type instead */
+ baseTypeId = getBaseTypeTypeMod(targetTypeId, &domainTypMod);
+
+
+ /*
+ * Use the domain typmod rather than what was supplied if the
+ * domain was empty. atttypmod will always be -1 if domains are in use.
+ */
+ if (baseTypeId != targetTypeId)
+ {
+ Assert(atttypmod < 0);
+ atttypmod = domainTypMod;
+ }
/*
* A negative typmod is assumed to mean that no coercion is wanted.
@@ -282,12 +312,8 @@ coerce_type_typmod(ParseState *pstate, Node *node,
if (atttypmod < 0 || atttypmod == exprTypmod(node))
return node;
- /* If given type is a domain, use base type instead */
- baseTypeId = getBaseType(targetTypeId);
-
/* Note this is always implicit coercion */
funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID, false);
-
if (OidIsValid(funcId))
{
Const *cons;
@@ -301,10 +327,6 @@ coerce_type_typmod(ParseState *pstate, Node *node,
false);
node = build_func_call(funcId, baseTypeId, makeList2(node, cons));
-
- /* relabel if it's domain case */
- if (targetTypeId != baseTypeId)
- node = (Node *) makeRelabelType(node, targetTypeId, atttypmod);
}
return node;
@@ -805,3 +827,58 @@ build_func_call(Oid funcid, Oid rettype, List *args)
return (Node *) expr;
}
+
+/*
+ * Create an expression tree to enforce the constraints (if any)
+ * which should be applied by the type.
+ */
+static Node *
+TypeConstraints(Node *arg, Oid typeId)
+{
+ char *notNull = NULL;
+
+ for (;;)
+ {
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ tup = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(typeId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "getBaseType: failed to lookup type %u", typeId);
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /* Test for NOT NULL Constraint */
+ if (typTup->typnotnull && notNull == NULL)
+ notNull = NameStr(typTup->typname);
+
+ /* TODO: Add CHECK Constraints to domains */
+
+ if (typTup->typtype != 'd')
+ {
+ /* Not a domain, so done */
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ typeId = typTup->typbasetype;
+ ReleaseSysCache(tup);
+ }
+
+ /*
+ * Only need to add one NOT NULL check regardless of how many
+ * domains in the tree request it.
+ */
+ if (notNull != NULL) {
+ Constraint *r = makeNode(Constraint);
+
+ r->raw_expr = arg;
+ r->contype = CONSTR_NOTNULL;
+ r->name = notNull;
+
+ arg = (Node *) r;
+ }
+
+ return arg;
+}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index ac4a5b6e5f3..d93aeb55b5d 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.120 2002/07/04 15:24:01 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.121 2002/07/06 20:16:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -864,7 +864,7 @@ exprType(Node *expr)
TargetEntry *tent;
if (!qtree || !IsA(qtree, Query))
- elog(ERROR, "Cannot get type for untransformed sublink");
+ elog(ERROR, "exprType: Cannot get type for untransformed sublink");
tent = (TargetEntry *) lfirst(qtree->targetList);
type = tent->resdom->restype;
}
@@ -881,6 +881,9 @@ exprType(Node *expr)
case T_CaseWhen:
type = exprType(((CaseWhen *) expr)->result);
break;
+ case T_Constraint:
+ type = exprType(((Constraint *) expr)->raw_expr);
+ break;
case T_NullTest:
type = BOOLOID;
break;
@@ -888,7 +891,7 @@ exprType(Node *expr)
type = BOOLOID;
break;
default:
- elog(ERROR, "Do not know how to get type for %d node",
+ elog(ERROR, "exprType: Do not know how to get type for %d node",
nodeTag(expr));
break;
}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 04cefb29904..14510a69b1b 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.43 2002/06/20 20:29:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.44 2002/07/06 20:16:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -450,7 +450,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
List *raw_parsetree_list;
SelectStmt *stmt;
ResTarget *restarget;
- A_Const *aconst;
+ TypeCast *typecast;
TypeName *typename;
initStringInfo(&buf);
@@ -463,7 +463,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
* paranoia is justified since the string might contain anything.
*/
if (length(raw_parsetree_list) != 1)
- elog(ERROR, "Invalid type name '%s'", str);
+ elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
stmt = (SelectStmt *) lfirst(raw_parsetree_list);
if (stmt == NULL ||
!IsA(stmt, SelectStmt) ||
@@ -479,25 +479,23 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
stmt->limitCount != NULL ||
stmt->forUpdate != NIL ||
stmt->op != SETOP_NONE)
- elog(ERROR, "Invalid type name '%s'", str);
+ elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
if (length(stmt->targetList) != 1)
- elog(ERROR, "Invalid type name '%s'", str);
+ elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
restarget = (ResTarget *) lfirst(stmt->targetList);
if (restarget == NULL ||
!IsA(restarget, ResTarget) ||
restarget->name != NULL ||
restarget->indirection != NIL)
- elog(ERROR, "Invalid type name '%s'", str);
- aconst = (A_Const *) restarget->val;
- if (aconst == NULL ||
- !IsA(aconst, A_Const) ||
- aconst->val.type != T_Null)
- elog(ERROR, "Invalid type name '%s'", str);
- typename = aconst->typename;
+ elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+ typecast = (TypeCast *) restarget->val;
+ if (typecast == NULL ||
+ !IsA(typecast->arg, A_Const))
+ elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
+ typename = typecast->typename;
if (typename == NULL ||
!IsA(typename, TypeName))
- elog(ERROR, "Invalid type name '%s'", str);
-
+ elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
*type_id = typenameTypeId(typename);
*typmod = typename->typmod;
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 233910a85c3..5063225afce 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
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.74 2002/06/20 20:29:39 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.75 2002/07/06 20:16:36 momjian Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -1058,6 +1058,43 @@ getBaseType(Oid typid)
}
/*
+ * getBaseTypeTypeMod
+ * If the given type is a domain, return its base type;
+ * otherwise return the type's own OID.
+ */
+Oid
+getBaseTypeTypeMod(Oid typid, int32 *typmod)
+{
+ /*
+ * We loop to find the bottom base type in a stack of domains.
+ */
+ for (;;)
+ {
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ tup = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(typid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "getBaseType: failed to lookup type %u", typid);
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+ if (typTup->typtype != 'd')
+ {
+ /* Not a domain, so done */
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ typid = typTup->typbasetype;
+ *typmod = typTup->typtypmod;
+ ReleaseSysCache(tup);
+ }
+
+ return typid;
+}
+
+/*
* get_typavgwidth
*
* Given a type OID and a typmod value (pass -1 if typmod is unknown),
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 32d5dc1744e..ca361ebc2cc 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lsyscache.h,v 1.53 2002/06/20 20:29:53 momjian Exp $
+ * $Id: lsyscache.h,v 1.54 2002/07/06 20:16:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,6 +52,7 @@ extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
extern char get_typstorage(Oid typid);
extern Node *get_typdefault(Oid typid);
extern Oid getBaseType(Oid typid);
+extern Oid getBaseTypeTypeMod(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 7127215869a..078d2386bf9 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -11,6 +11,15 @@ create domain domainvarchar varchar(5);
create domain domainnumeric numeric(8,2);
create domain domainint4 int4;
create domain domaintext text;
+-- Test coercions
+SELECT cast('123456' as domainvarchar); -- fail
+ERROR: value too long for type character varying(5)
+SELECT cast('12345' as domainvarchar); -- pass
+ domainvarchar
+---------------
+ 12345
+(1 row)
+
-- Test tables using domains
create table basictest
( testint4 domainint4
@@ -80,7 +89,7 @@ drop table domarrtest;
drop domain domainint4arr restrict;
drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL;
-create domain dnull varchar(15) NULL;
+create domain dnull varchar(15);
create table nulltest
( col1 dnotnull
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
@@ -88,12 +97,12 @@ create table nulltest
, col4 dnull
);
INSERT INTO nulltest DEFAULT VALUES;
-ERROR: ExecAppend: Fail to add null value in not null attribute col1
+ERROR: ExecAppend: Fail to add null value in not null attribute col3
INSERT INTO nulltest values ('a', 'b', 'c', 'd'); -- Good
INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
-ERROR: ExecAppend: Fail to add null value in not null attribute col1
+ERROR: Domain dnotnull does not allow NULL values
INSERT INTO nulltest values ('a', NULL, 'c', 'd');
-ERROR: ExecAppend: Fail to add null value in not null attribute col2
+ERROR: Domain dnotnull does not allow NULL values
INSERT INTO nulltest values ('a', 'b', NULL, 'd');
ERROR: ExecAppend: Fail to add null value in not null attribute col3
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
@@ -104,6 +113,20 @@ select * from nulltest;
a | b | c |
(2 rows)
+-- Test out coerced (casted) constraints
+SELECT cast('1' as dnotnull);
+ dnotnull
+----------
+ 1
+(1 row)
+
+SELECT cast(NULL as dnotnull); -- fail
+ERROR: Domain dnotnull does not allow NULL values
+SELECT cast(cast(NULL as dnull) as dnotnull); -- fail
+ERROR: Domain dnotnull does not allow NULL values
+SELECT cast(col4 as dnotnull) from nulltest; -- fail
+ERROR: Domain dnotnull does not allow NULL values
+-- cleanup
drop table nulltest;
drop domain dnotnull restrict;
drop domain dnull restrict;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index cecb876c386..055c377decb 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -17,6 +17,9 @@ create domain domainnumeric numeric(8,2);
create domain domainint4 int4;
create domain domaintext text;
+-- Test coercions
+SELECT cast('123456' as domainvarchar); -- fail
+SELECT cast('12345' as domainvarchar); -- pass
-- Test tables using domains
create table basictest
@@ -65,7 +68,7 @@ drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL;
-create domain dnull varchar(15) NULL;
+create domain dnull varchar(15);
create table nulltest
( col1 dnotnull
@@ -81,6 +84,13 @@ INSERT INTO nulltest values ('a', 'b', NULL, 'd');
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
select * from nulltest;
+-- Test out coerced (casted) constraints
+SELECT cast('1' as dnotnull);
+SELECT cast(NULL as dnotnull); -- fail
+SELECT cast(cast(NULL as dnull) as dnotnull); -- fail
+SELECT cast(col4 as dnotnull) from nulltest; -- fail
+
+-- cleanup
drop table nulltest;
drop domain dnotnull restrict;
drop domain dnull restrict;