mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
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
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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"
|
#include "utils/relcache.h"
|
||||||
|
|
||||||
|
|
||||||
static List *MergeDomainAttributes(List *schema);
|
|
||||||
static List *MergeAttributes(List *schema, List *supers, bool istemp,
|
static List *MergeAttributes(List *schema, List *supers, bool istemp,
|
||||||
List **supOids, List **supconstr, bool *supHasOids);
|
List **supOids, List **supconstr, bool *supHasOids);
|
||||||
static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
|
static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
|
||||||
@ -122,13 +121,6 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
|||||||
aclcheck_error(aclresult, get_namespace_name(namespaceId));
|
aclcheck_error(aclresult, get_namespace_name(namespaceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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,
|
* Look up inheritance ancestors and generate relation schema,
|
||||||
* including inherited attributes.
|
* including inherited attributes.
|
||||||
@ -328,49 +320,6 @@ TruncateRelation(const RangeVar *relation)
|
|||||||
heap_truncate(relid);
|
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
|
* MergeAttributes
|
||||||
* Returns new schema given initial schema and superclasses.
|
* Returns new schema given initial schema and superclasses.
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
|
static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
|
||||||
bool *isNull, ExprDoneCond *isDone);
|
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
|
* ExecEvalBooleanTest
|
||||||
*
|
*
|
||||||
@ -1473,6 +1512,12 @@ ExecEvalExpr(Node *expression,
|
|||||||
isNull,
|
isNull,
|
||||||
isDone);
|
isDone);
|
||||||
break;
|
break;
|
||||||
|
case T_Constraint:
|
||||||
|
retDatum = ExecEvalConstraint((Constraint *) expression,
|
||||||
|
econtext,
|
||||||
|
isNull,
|
||||||
|
isDone);
|
||||||
|
break;
|
||||||
case T_CaseExpr:
|
case T_CaseExpr:
|
||||||
retDatum = ExecEvalCase((CaseExpr *) expression,
|
retDatum = ExecEvalCase((CaseExpr *) expression,
|
||||||
econtext,
|
econtext,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -1882,6 +1882,8 @@ expression_tree_walker(Node *node,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case T_Constraint:
|
||||||
|
return walker(((Constraint *) node)->raw_expr, context);
|
||||||
case T_NullTest:
|
case T_NullTest:
|
||||||
return walker(((NullTest *) node)->arg, context);
|
return walker(((NullTest *) node)->arg, context);
|
||||||
case T_BooleanTest:
|
case T_BooleanTest:
|
||||||
@ -2236,6 +2238,20 @@ expression_tree_mutator(Node *node,
|
|||||||
return (Node *) newnode;
|
return (Node *) newnode;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case T_NullTest:
|
||||||
{
|
{
|
||||||
NullTest *ntest = (NullTest *) node;
|
NullTest *ntest = (NullTest *) node;
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -6968,24 +6968,16 @@ static Node *
|
|||||||
makeTypeCast(Node *arg, TypeName *typename)
|
makeTypeCast(Node *arg, TypeName *typename)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If arg is an A_Const, just stick the typename into the
|
* Simply generate a TypeCast node.
|
||||||
* field reserved for it --- unless there's something there already!
|
*
|
||||||
* (We don't want to collapse x::type1::type2 into just x::type2.)
|
* Earlier we would determine whether an A_Const would
|
||||||
* Otherwise, generate a TypeCast node.
|
* be acceptable, however Domains require coerce_type()
|
||||||
|
* to process them -- applying constraints as required.
|
||||||
*/
|
*/
|
||||||
if (IsA(arg, A_Const) &&
|
TypeCast *n = makeNode(TypeCast);
|
||||||
((A_Const *) arg)->typename == NULL)
|
n->arg = arg;
|
||||||
{
|
n->typename = typename;
|
||||||
((A_Const *) arg)->typename = typename;
|
return (Node *) n;
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TypeCast *n = makeNode(TypeCast);
|
|
||||||
n->arg = arg;
|
|
||||||
n->typename = typename;
|
|
||||||
return (Node *) n;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Node *
|
static Node *
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 Node *build_func_call(Oid funcid, Oid rettype, List *args);
|
||||||
static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
|
static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
|
||||||
Oid secondArgType, bool isExplicit);
|
Oid secondArgType, bool isExplicit);
|
||||||
|
static Node *TypeConstraints(Node *arg, Oid typeId);
|
||||||
|
|
||||||
/* coerce_type()
|
/* coerce_type()
|
||||||
* Convert a function argument to a different type.
|
* Convert a function argument to a different type.
|
||||||
@ -48,7 +48,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
|||||||
targetTypeId == InvalidOid ||
|
targetTypeId == InvalidOid ||
|
||||||
node == NULL)
|
node == NULL)
|
||||||
{
|
{
|
||||||
/* no conversion needed */
|
/* no conversion needed, but constraints may need to be applied */
|
||||||
result = node;
|
result = node;
|
||||||
}
|
}
|
||||||
else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
|
else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
|
||||||
@ -72,6 +72,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
|||||||
Const *con = (Const *) node;
|
Const *con = (Const *) node;
|
||||||
Const *newcon = makeNode(Const);
|
Const *newcon = makeNode(Const);
|
||||||
Type targetType = typeidType(targetTypeId);
|
Type targetType = typeidType(targetTypeId);
|
||||||
|
Oid baseTypeId = getBaseType(targetTypeId);
|
||||||
|
|
||||||
newcon->consttype = targetTypeId;
|
newcon->consttype = targetTypeId;
|
||||||
newcon->constlen = typeLen(targetType);
|
newcon->constlen = typeLen(targetType);
|
||||||
@ -83,14 +84,16 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
|||||||
{
|
{
|
||||||
char *val = DatumGetCString(DirectFunctionCall1(unknownout,
|
char *val = DatumGetCString(DirectFunctionCall1(unknownout,
|
||||||
con->constvalue));
|
con->constvalue));
|
||||||
|
|
||||||
newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
|
newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
|
||||||
pfree(val);
|
pfree(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseSysCache(targetType);
|
ReleaseSysCache(targetType);
|
||||||
|
|
||||||
|
/* Test for domain, and apply appropriate constraints */
|
||||||
result = (Node *) newcon;
|
result = (Node *) newcon;
|
||||||
|
if (targetTypeId != baseTypeId)
|
||||||
|
result = (Node *) TypeConstraints(result, targetTypeId);
|
||||||
}
|
}
|
||||||
else if (IsBinaryCompatible(inputTypeId, 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?
|
* default -1 typmod, to save a possible length-coercion later?
|
||||||
* Would work if both types have same interpretation of typmod,
|
* Would work if both types have same interpretation of typmod,
|
||||||
* which is likely but not certain.
|
* 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))
|
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.
|
* For domains, we use the coercion function for the base type.
|
||||||
*/
|
*/
|
||||||
Oid baseTypeId = getBaseType(targetTypeId);
|
|
||||||
Oid funcId;
|
Oid funcId;
|
||||||
|
Oid baseTypeId = getBaseType(targetTypeId);
|
||||||
|
|
||||||
funcId = find_coercion_function(baseTypeId,
|
funcId = find_coercion_function(baseTypeId,
|
||||||
getBaseType(inputTypeId),
|
getBaseType(inputTypeId),
|
||||||
@ -138,9 +150,12 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
|||||||
|
|
||||||
result = build_func_call(funcId, baseTypeId, makeList1(node));
|
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)
|
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
|
* 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 baseTypeId;
|
||||||
Oid funcId;
|
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.
|
* 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))
|
if (atttypmod < 0 || atttypmod == exprTypmod(node))
|
||||||
return node;
|
return node;
|
||||||
|
|
||||||
/* If given type is a domain, use base type instead */
|
|
||||||
baseTypeId = getBaseType(targetTypeId);
|
|
||||||
|
|
||||||
/* Note this is always implicit coercion */
|
/* Note this is always implicit coercion */
|
||||||
funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID, false);
|
funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID, false);
|
||||||
|
|
||||||
if (OidIsValid(funcId))
|
if (OidIsValid(funcId))
|
||||||
{
|
{
|
||||||
Const *cons;
|
Const *cons;
|
||||||
@ -301,10 +327,6 @@ coerce_type_typmod(ParseState *pstate, Node *node,
|
|||||||
false);
|
false);
|
||||||
|
|
||||||
node = build_func_call(funcId, baseTypeId, makeList2(node, cons));
|
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;
|
return node;
|
||||||
@ -805,3 +827,58 @@ build_func_call(Oid funcid, Oid rettype, List *args)
|
|||||||
|
|
||||||
return (Node *) expr;
|
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;
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
TargetEntry *tent;
|
||||||
|
|
||||||
if (!qtree || !IsA(qtree, Query))
|
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);
|
tent = (TargetEntry *) lfirst(qtree->targetList);
|
||||||
type = tent->resdom->restype;
|
type = tent->resdom->restype;
|
||||||
}
|
}
|
||||||
@ -881,6 +881,9 @@ exprType(Node *expr)
|
|||||||
case T_CaseWhen:
|
case T_CaseWhen:
|
||||||
type = exprType(((CaseWhen *) expr)->result);
|
type = exprType(((CaseWhen *) expr)->result);
|
||||||
break;
|
break;
|
||||||
|
case T_Constraint:
|
||||||
|
type = exprType(((Constraint *) expr)->raw_expr);
|
||||||
|
break;
|
||||||
case T_NullTest:
|
case T_NullTest:
|
||||||
type = BOOLOID;
|
type = BOOLOID;
|
||||||
break;
|
break;
|
||||||
@ -888,7 +891,7 @@ exprType(Node *expr)
|
|||||||
type = BOOLOID;
|
type = BOOLOID;
|
||||||
break;
|
break;
|
||||||
default:
|
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));
|
nodeTag(expr));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
List *raw_parsetree_list;
|
||||||
SelectStmt *stmt;
|
SelectStmt *stmt;
|
||||||
ResTarget *restarget;
|
ResTarget *restarget;
|
||||||
A_Const *aconst;
|
TypeCast *typecast;
|
||||||
TypeName *typename;
|
TypeName *typename;
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
@ -463,7 +463,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
|
|||||||
* paranoia is justified since the string might contain anything.
|
* paranoia is justified since the string might contain anything.
|
||||||
*/
|
*/
|
||||||
if (length(raw_parsetree_list) != 1)
|
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);
|
stmt = (SelectStmt *) lfirst(raw_parsetree_list);
|
||||||
if (stmt == NULL ||
|
if (stmt == NULL ||
|
||||||
!IsA(stmt, SelectStmt) ||
|
!IsA(stmt, SelectStmt) ||
|
||||||
@ -479,25 +479,23 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
|
|||||||
stmt->limitCount != NULL ||
|
stmt->limitCount != NULL ||
|
||||||
stmt->forUpdate != NIL ||
|
stmt->forUpdate != NIL ||
|
||||||
stmt->op != SETOP_NONE)
|
stmt->op != SETOP_NONE)
|
||||||
elog(ERROR, "Invalid type name '%s'", str);
|
elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
|
||||||
if (length(stmt->targetList) != 1)
|
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);
|
restarget = (ResTarget *) lfirst(stmt->targetList);
|
||||||
if (restarget == NULL ||
|
if (restarget == NULL ||
|
||||||
!IsA(restarget, ResTarget) ||
|
!IsA(restarget, ResTarget) ||
|
||||||
restarget->name != NULL ||
|
restarget->name != NULL ||
|
||||||
restarget->indirection != NIL)
|
restarget->indirection != NIL)
|
||||||
elog(ERROR, "Invalid type name '%s'", str);
|
elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
|
||||||
aconst = (A_Const *) restarget->val;
|
typecast = (TypeCast *) restarget->val;
|
||||||
if (aconst == NULL ||
|
if (typecast == NULL ||
|
||||||
!IsA(aconst, A_Const) ||
|
!IsA(typecast->arg, A_Const))
|
||||||
aconst->val.type != T_Null)
|
elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
|
||||||
elog(ERROR, "Invalid type name '%s'", str);
|
typename = typecast->typename;
|
||||||
typename = aconst->typename;
|
|
||||||
if (typename == NULL ||
|
if (typename == NULL ||
|
||||||
!IsA(typename, TypeName))
|
!IsA(typename, TypeName))
|
||||||
elog(ERROR, "Invalid type name '%s'", str);
|
elog(ERROR, "parseTypeString: Invalid type name '%s'", str);
|
||||||
|
|
||||||
*type_id = typenameTypeId(typename);
|
*type_id = typenameTypeId(typename);
|
||||||
*typmod = typename->typmod;
|
*typmod = typename->typmod;
|
||||||
|
|
||||||
|
39
src/backend/utils/cache/lsyscache.c
vendored
39
src/backend/utils/cache/lsyscache.c
vendored
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Eventually, the index information should go through here, too.
|
* Eventually, the index information should go through here, too.
|
||||||
@ -1057,6 +1057,43 @@ getBaseType(Oid typid)
|
|||||||
return typid;
|
return 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
|
* get_typavgwidth
|
||||||
*
|
*
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 char get_typstorage(Oid typid);
|
||||||
extern Node *get_typdefault(Oid typid);
|
extern Node *get_typdefault(Oid typid);
|
||||||
extern Oid getBaseType(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_typavgwidth(Oid typid, int32 typmod);
|
||||||
extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
|
extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
|
||||||
extern bool get_attstatsslot(HeapTuple statstuple,
|
extern bool get_attstatsslot(HeapTuple statstuple,
|
||||||
|
@ -11,6 +11,15 @@ create domain domainvarchar varchar(5);
|
|||||||
create domain domainnumeric numeric(8,2);
|
create domain domainnumeric numeric(8,2);
|
||||||
create domain domainint4 int4;
|
create domain domainint4 int4;
|
||||||
create domain domaintext text;
|
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
|
-- Test tables using domains
|
||||||
create table basictest
|
create table basictest
|
||||||
( testint4 domainint4
|
( testint4 domainint4
|
||||||
@ -80,7 +89,7 @@ drop table domarrtest;
|
|||||||
drop domain domainint4arr restrict;
|
drop domain domainint4arr restrict;
|
||||||
drop domain domaintextarr restrict;
|
drop domain domaintextarr restrict;
|
||||||
create domain dnotnull varchar(15) NOT NULL;
|
create domain dnotnull varchar(15) NOT NULL;
|
||||||
create domain dnull varchar(15) NULL;
|
create domain dnull varchar(15);
|
||||||
create table nulltest
|
create table nulltest
|
||||||
( col1 dnotnull
|
( col1 dnotnull
|
||||||
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
|
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
|
||||||
@ -88,12 +97,12 @@ create table nulltest
|
|||||||
, col4 dnull
|
, col4 dnull
|
||||||
);
|
);
|
||||||
INSERT INTO nulltest DEFAULT VALUES;
|
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 ('a', 'b', 'c', 'd'); -- Good
|
||||||
INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
|
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');
|
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');
|
INSERT INTO nulltest values ('a', 'b', NULL, 'd');
|
||||||
ERROR: ExecAppend: Fail to add null value in not null attribute col3
|
ERROR: ExecAppend: Fail to add null value in not null attribute col3
|
||||||
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
|
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
|
||||||
@ -104,6 +113,20 @@ select * from nulltest;
|
|||||||
a | b | c |
|
a | b | c |
|
||||||
(2 rows)
|
(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 table nulltest;
|
||||||
drop domain dnotnull restrict;
|
drop domain dnotnull restrict;
|
||||||
drop domain dnull restrict;
|
drop domain dnull restrict;
|
||||||
|
@ -17,6 +17,9 @@ create domain domainnumeric numeric(8,2);
|
|||||||
create domain domainint4 int4;
|
create domain domainint4 int4;
|
||||||
create domain domaintext text;
|
create domain domaintext text;
|
||||||
|
|
||||||
|
-- Test coercions
|
||||||
|
SELECT cast('123456' as domainvarchar); -- fail
|
||||||
|
SELECT cast('12345' as domainvarchar); -- pass
|
||||||
|
|
||||||
-- Test tables using domains
|
-- Test tables using domains
|
||||||
create table basictest
|
create table basictest
|
||||||
@ -65,7 +68,7 @@ drop domain domaintextarr restrict;
|
|||||||
|
|
||||||
|
|
||||||
create domain dnotnull varchar(15) NOT NULL;
|
create domain dnotnull varchar(15) NOT NULL;
|
||||||
create domain dnull varchar(15) NULL;
|
create domain dnull varchar(15);
|
||||||
|
|
||||||
create table nulltest
|
create table nulltest
|
||||||
( col1 dnotnull
|
( col1 dnotnull
|
||||||
@ -81,6 +84,13 @@ INSERT INTO nulltest values ('a', 'b', NULL, 'd');
|
|||||||
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
|
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
|
||||||
select * from nulltest;
|
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 table nulltest;
|
||||||
drop domain dnotnull restrict;
|
drop domain dnotnull restrict;
|
||||||
drop domain dnull restrict;
|
drop domain dnull restrict;
|
||||||
|
Reference in New Issue
Block a user