diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/tablecmds.c | 53 | ||||
-rw-r--r-- | src/backend/executor/execQual.c | 47 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 18 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 28 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 109 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 9 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 26 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 39 | ||||
-rw-r--r-- | src/include/utils/lsyscache.h | 3 | ||||
-rw-r--r-- | src/test/regress/expected/domain.out | 31 | ||||
-rw-r--r-- | src/test/regress/sql/domain.sql | 12 |
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; |