mirror of
https://github.com/postgres/postgres.git
synced 2025-08-22 21:53:06 +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:
@@ -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 *
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user