domains-over-composites-1.patch
text/x-diff
Filename: domains-over-composites-1.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/catalog/pg_inherits.c | 0 | 0 |
| src/backend/catalog/pg_proc.c | 0 | 0 |
| src/backend/commands/typecmds.c | 0 | 0 |
| src/backend/parser/parse_coerce.c | 0 | 0 |
| src/backend/parser/parse_func.c | 0 | 0 |
| src/backend/parser/parse_target.c | 0 | 0 |
| src/backend/parser/parse_type.c | 0 | 0 |
| src/backend/utils/adt/ruleutils.c | 0 | 0 |
| src/backend/utils/fmgr/funcapi.c | 0 | 0 |
| src/include/funcapi.h | 0 | 0 |
| src/include/parser/parse_type.h | 0 | 0 |
| src/test/regress/expected/domain.out | 0 | 0 |
| src/test/regress/sql/domain.sql | 0 | 0 |
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index 245a374..1bd8a58 100644
*** a/src/backend/catalog/pg_inherits.c
--- b/src/backend/catalog/pg_inherits.c
*************** has_superclass(Oid relationId)
*** 301,306 ****
--- 301,311 ----
/*
* Given two type OIDs, determine whether the first is a complex type
* (class type) that inherits from the second.
+ *
+ * This essentially asks whether the first type is guaranteed to be coercible
+ * to the second. Therefore, we allow the first type to be a domain over a
+ * complex type that inherits from the second; that creates no difficulties.
+ * But the second type cannot be a domain.
*/
bool
typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
*************** typeInheritsFrom(Oid subclassTypeId, Oid
*** 314,322 ****
ListCell *queue_item;
/* We need to work with the associated relation OIDs */
! subclassRelid = typeidTypeRelid(subclassTypeId);
if (subclassRelid == InvalidOid)
! return false; /* not a complex type */
superclassRelid = typeidTypeRelid(superclassTypeId);
if (superclassRelid == InvalidOid)
return false; /* not a complex type */
--- 319,327 ----
ListCell *queue_item;
/* We need to work with the associated relation OIDs */
! subclassRelid = typeOrDomainTypeRelid(subclassTypeId);
if (subclassRelid == InvalidOid)
! return false; /* not a complex type or domain over one */
superclassRelid = typeidTypeRelid(superclassTypeId);
if (superclassRelid == InvalidOid)
return false; /* not a complex type */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 571856e..47916cf 100644
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
*************** ProcedureCreate(const char *procedureNam
*** 262,268 ****
*/
if (parameterCount == 1 &&
OidIsValid(parameterTypes->values[0]) &&
! (relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid &&
get_attnum(relid, procedureName) != InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
--- 262,268 ----
*/
if (parameterCount == 1 &&
OidIsValid(parameterTypes->values[0]) &&
! (relid = typeOrDomainTypeRelid(parameterTypes->values[0])) != InvalidOid &&
get_attnum(relid, procedureName) != InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c2fc59d..0a08ef5 100644
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** DefineDomain(CreateDomainStmt *stmt)
*** 796,808 ****
basetypeoid = HeapTupleGetOid(typeTup);
/*
! * Base type must be a plain base type, another domain, an enum or a range
! * type. Domains over pseudotypes would create a security hole. Domains
! * over composite types might be made to work in the future, but not
! * today.
*/
typtype = baseType->typtype;
if (typtype != TYPTYPE_BASE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
typtype != TYPTYPE_RANGE)
--- 796,811 ----
basetypeoid = HeapTupleGetOid(typeTup);
/*
! * Base type must be a plain base type, a composite type, another domain,
! * an enum or a range type. Domains over pseudotypes would create a
! * security hole. (It would be shorter to code this to just check for
! * pseudotypes; but it seems safer to call out the specific typtypes that
! * are supported, rather than assume that all future typtypes would be
! * automatically supported.)
*/
typtype = baseType->typtype;
if (typtype != TYPTYPE_BASE &&
+ typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
typtype != TYPTYPE_RANGE)
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 0bc7dba..7b16d6c 100644
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
*************** coerce_type(ParseState *pstate, Node *no
*** 500,508 ****
--- 500,525 ----
* Input class type is a subclass of target, so generate an
* appropriate runtime conversion (removing unneeded columns and
* possibly rearranging the ones that are wanted).
+ *
+ * We will also get here when the input is a domain over a subclass of
+ * the target type. To keep life simple for the executor, we define
+ * ConvertRowtypeExpr as only working between regular composite types;
+ * therefore, in such cases insert a RelabelType to smash the input
+ * expression down to its base type.
*/
+ Oid baseTypeId = getBaseType(inputTypeId);
ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
+ if (baseTypeId != inputTypeId)
+ {
+ RelabelType *rt = makeRelabelType((Expr *) node,
+ baseTypeId, -1,
+ InvalidOid,
+ COERCE_IMPLICIT_CAST);
+
+ rt->location = location;
+ node = (Node *) rt;
+ }
r->arg = (Expr *) node;
r->resulttype = targetTypeId;
r->convertformat = cformat;
*************** coerce_record_to_complex(ParseState *pst
*** 938,943 ****
--- 955,962 ----
int location)
{
RowExpr *rowexpr;
+ Oid baseTypeId;
+ int32 baseTypeMod = -1;
TupleDesc tupdesc;
List *args = NIL;
List *newargs;
*************** coerce_record_to_complex(ParseState *pst
*** 973,979 ****
format_type_be(targetTypeId)),
parser_coercion_errposition(pstate, location, node)));
! tupdesc = lookup_rowtype_tupdesc(targetTypeId, -1);
newargs = NIL;
ucolno = 1;
arg = list_head(args);
--- 992,1005 ----
format_type_be(targetTypeId)),
parser_coercion_errposition(pstate, location, node)));
! /*
! * Look up the composite type, accounting for possibility that what we are
! * given is a domain over composite.
! */
! baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
! tupdesc = lookup_rowtype_tupdesc(baseTypeId, baseTypeMod);
!
! /* Process the fields */
newargs = NIL;
ucolno = 1;
arg = list_head(args);
*************** coerce_record_to_complex(ParseState *pst
*** 1041,1050 ****
rowexpr = makeNode(RowExpr);
rowexpr->args = newargs;
! rowexpr->row_typeid = targetTypeId;
rowexpr->row_format = cformat;
rowexpr->colnames = NIL; /* not needed for named target type */
rowexpr->location = location;
return (Node *) rowexpr;
}
--- 1067,1087 ----
rowexpr = makeNode(RowExpr);
rowexpr->args = newargs;
! rowexpr->row_typeid = baseTypeId;
rowexpr->row_format = cformat;
rowexpr->colnames = NIL; /* not needed for named target type */
rowexpr->location = location;
+
+ /* If target is a domain, apply constraints */
+ if (baseTypeId != targetTypeId)
+ {
+ rowexpr->row_format = COERCE_IMPLICIT_CAST;
+ return coerce_to_domain((Node *) rowexpr,
+ baseTypeId, baseTypeMod,
+ targetTypeId,
+ cformat, location, true, false);
+ }
+
return (Node *) rowexpr;
}
*************** is_complex_array(Oid typid)
*** 2379,2391 ****
/*
* Check whether reltypeId is the row type of a typed table of type
! * reloftypeId. (This is conceptually similar to the subtype
! * relationship checked by typeInheritsFrom().)
*/
static bool
typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
{
! Oid relid = typeidTypeRelid(reltypeId);
bool result = false;
if (relid)
--- 2416,2428 ----
/*
* Check whether reltypeId is the row type of a typed table of type
! * reloftypeId, or is a domain over such a row type. (This is conceptually
! * similar to the subtype relationship checked by typeInheritsFrom().)
*/
static bool
typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
{
! Oid relid = typeOrDomainTypeRelid(reltypeId);
bool result = false;
if (relid)
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 8487eda..7570108 100644
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
*************** ParseComplexProjection(ParseState *pstat
*** 1819,1836 ****
}
/*
! * Else do it the hard way with get_expr_result_type().
*
* If it's a Var of type RECORD, we have to work even harder: we have to
! * find what the Var refers to, and pass that to get_expr_result_type.
* That task is handled by expandRecordVariable().
*/
if (IsA(first_arg, Var) &&
((Var *) first_arg)->vartype == RECORDOID)
tupdesc = expandRecordVariable(pstate, (Var *) first_arg, 0);
! else if (get_expr_result_type(first_arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
return NULL; /* unresolvable RECORD type */
- Assert(tupdesc);
for (i = 0; i < tupdesc->natts; i++)
{
--- 1819,1837 ----
}
/*
! * Else do it the hard way with get_expr_result_tupdesc().
*
* If it's a Var of type RECORD, we have to work even harder: we have to
! * find what the Var refers to, and pass that to get_expr_result_tupdesc.
* That task is handled by expandRecordVariable().
*/
if (IsA(first_arg, Var) &&
((Var *) first_arg)->vartype == RECORDOID)
tupdesc = expandRecordVariable(pstate, (Var *) first_arg, 0);
! else
! tupdesc = get_expr_result_tupdesc(first_arg, true);
! if (!tupdesc)
return NULL; /* unresolvable RECORD type */
for (i = 0; i < tupdesc->natts; i++)
{
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 0a70539..d689e2a 100644
*** a/src/backend/parser/parse_target.c
--- b/src/backend/parser/parse_target.c
*************** transformAssignmentIndirection(ParseStat
*** 725,730 ****
--- 725,732 ----
else
{
FieldStore *fstore;
+ Oid baseTypeId;
+ int32 baseTypeMod;
Oid typrelid;
AttrNumber attnum;
Oid fieldTypeId;
*************** transformAssignmentIndirection(ParseStat
*** 752,758 ****
/* No subscripts, so can process field selection here */
! typrelid = typeidTypeRelid(targetTypeId);
if (!typrelid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
--- 754,767 ----
/* No subscripts, so can process field selection here */
! /*
! * Look up the composite type, accounting for possibility that
! * what we are given is a domain over composite.
! */
! baseTypeMod = targetTypMod;
! baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
!
! typrelid = typeidTypeRelid(baseTypeId);
if (!typrelid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
*************** transformAssignmentIndirection(ParseStat
*** 796,802 ****
fstore->arg = (Expr *) basenode;
fstore->newvals = list_make1(rhs);
fstore->fieldnums = list_make1_int(attnum);
! fstore->resulttype = targetTypeId;
return (Node *) fstore;
}
--- 805,819 ----
fstore->arg = (Expr *) basenode;
fstore->newvals = list_make1(rhs);
fstore->fieldnums = list_make1_int(attnum);
! fstore->resulttype = baseTypeId;
!
! /* If target is a domain, apply constraints */
! if (baseTypeId != targetTypeId)
! return coerce_to_domain((Node *) fstore,
! baseTypeId, baseTypeMod,
! targetTypeId,
! COERCE_IMPLICIT_CAST, location,
! false, false);
return (Node *) fstore;
}
*************** ExpandRowReference(ParseState *pstate, N
*** 1385,1406 ****
* (This can be pretty inefficient if the expression involves nontrivial
* computation :-(.)
*
! * Verify it's a composite type, and get the tupdesc. We use
! * get_expr_result_type() because that can handle references to functions
! * returning anonymous record types. If that fails, use
! * lookup_rowtype_tupdesc(), which will almost certainly fail as well, but
! * it will give an appropriate error message.
*
* If it's a Var of type RECORD, we have to work even harder: we have to
! * find what the Var refers to, and pass that to get_expr_result_type.
* That task is handled by expandRecordVariable().
*/
if (IsA(expr, Var) &&
((Var *) expr)->vartype == RECORDOID)
tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0);
! else if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
! tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
! exprTypmod(expr));
Assert(tupleDesc);
/* Generate a list of references to the individual fields */
--- 1402,1419 ----
* (This can be pretty inefficient if the expression involves nontrivial
* computation :-(.)
*
! * Verify it's a composite type, and get the tupdesc.
! * get_expr_result_tupdesc() handles this conveniently.
*
* If it's a Var of type RECORD, we have to work even harder: we have to
! * find what the Var refers to, and pass that to get_expr_result_tupdesc.
* That task is handled by expandRecordVariable().
*/
if (IsA(expr, Var) &&
((Var *) expr)->vartype == RECORDOID)
tupleDesc = expandRecordVariable(pstate, (Var *) expr, 0);
! else
! tupleDesc = get_expr_result_tupdesc(expr, false);
Assert(tupleDesc);
/* Generate a list of references to the individual fields */
*************** expandRecordVariable(ParseState *pstate,
*** 1608,1622 ****
/*
* We now have an expression we can't expand any more, so see if
! * get_expr_result_type() can do anything with it. If not, pass to
! * lookup_rowtype_tupdesc() which will probably fail, but will give an
! * appropriate error message while failing.
*/
! if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
! tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
! exprTypmod(expr));
!
! return tupleDesc;
}
--- 1621,1629 ----
/*
* We now have an expression we can't expand any more, so see if
! * get_expr_result_tupdesc() can do anything with it.
*/
! return get_expr_result_tupdesc(expr, false);
}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index d0b3fbe..b032651 100644
*** a/src/backend/parser/parse_type.c
--- b/src/backend/parser/parse_type.c
*************** stringTypeDatum(Type tp, char *string, i
*** 641,647 ****
return OidInputFunctionCall(typinput, string, typioparam, atttypmod);
}
! /* given a typeid, return the type's typrelid (associated relation, if any) */
Oid
typeidTypeRelid(Oid type_id)
{
--- 641,650 ----
return OidInputFunctionCall(typinput, string, typioparam, atttypmod);
}
! /*
! * Given a typeid, return the type's typrelid (associated relation), if any.
! * Returns InvalidOid if type is not a composite type.
! */
Oid
typeidTypeRelid(Oid type_id)
{
*************** typeidTypeRelid(Oid type_id)
*** 652,658 ****
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", type_id);
-
type = (Form_pg_type) GETSTRUCT(typeTuple);
result = type->typrelid;
ReleaseSysCache(typeTuple);
--- 655,660 ----
*************** typeidTypeRelid(Oid type_id)
*** 660,665 ****
--- 662,699 ----
}
/*
+ * Given a typeid, return the type's typrelid (associated relation), if any.
+ * Returns InvalidOid if type is not a composite type or a domain over one.
+ * This is the same as typeidTypeRelid(getBaseType(type_id)), but faster.
+ */
+ Oid
+ typeOrDomainTypeRelid(Oid type_id)
+ {
+ HeapTuple typeTuple;
+ Form_pg_type type;
+ Oid result;
+
+ for (;;)
+ {
+ typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", type_id);
+ type = (Form_pg_type) GETSTRUCT(typeTuple);
+ if (type->typtype != TYPTYPE_DOMAIN)
+ {
+ /* Not a domain, so done looking through domains */
+ break;
+ }
+ /* It is a domain, so examine the base type instead */
+ type_id = type->typbasetype;
+ ReleaseSysCache(typeTuple);
+ }
+ result = type->typrelid;
+ ReleaseSysCache(typeTuple);
+ return result;
+ }
+
+ /*
* error context callback for parse failure during parseTypeString()
*/
static void
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5a4adbd..9d069b5 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** get_name_for_var_field(Var *var, int fie
*** 6706,6722 ****
/*
* If it's a Var of type RECORD, we have to find what the Var refers to;
! * if not, we can use get_expr_result_type. If that fails, we try
! * lookup_rowtype_tupdesc, which will probably fail too, but will ereport
! * an acceptable message.
*/
if (!IsA(var, Var) ||
var->vartype != RECORDOID)
{
! if (get_expr_result_type((Node *) var, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
! tupleDesc = lookup_rowtype_tupdesc_copy(exprType((Node *) var),
! exprTypmod((Node *) var));
! Assert(tupleDesc);
/* Got the tupdesc, so we can extract the field name */
Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
--- 6706,6717 ----
/*
* If it's a Var of type RECORD, we have to find what the Var refers to;
! * if not, we can use get_expr_result_tupdesc().
*/
if (!IsA(var, Var) ||
var->vartype != RECORDOID)
{
! tupleDesc = get_expr_result_tupdesc((Node *) var, false);
/* Got the tupdesc, so we can extract the field name */
Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
*************** get_name_for_var_field(Var *var, int fie
*** 7019,7032 ****
/*
* We now have an expression we can't expand any more, so see if
! * get_expr_result_type() can do anything with it. If not, pass to
! * lookup_rowtype_tupdesc() which will probably fail, but will give an
! * appropriate error message while failing.
*/
! if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
! tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr),
! exprTypmod(expr));
! Assert(tupleDesc);
/* Got the tupdesc, so we can extract the field name */
Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
--- 7014,7022 ----
/*
* We now have an expression we can't expand any more, so see if
! * get_expr_result_tupdesc() can do anything with it.
*/
! tupleDesc = get_expr_result_tupdesc(expr, false);
/* Got the tupdesc, so we can extract the field name */
Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
return NameStr(tupleDesc->attrs[fieldno - 1]->attname);
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index be47d41..f36be55 100644
*** a/src/backend/utils/fmgr/funcapi.c
--- b/src/backend/utils/fmgr/funcapi.c
*************** internal_get_result_type(Oid funcid,
*** 394,399 ****
--- 394,474 ----
}
/*
+ * get_expr_result_tupdesc
+ * Get a tupdesc describing the result of a composite-valued expression
+ *
+ * If expression is not composite or rowtype can't be determined, returns NULL
+ * if noError is true, else throws error.
+ *
+ * This can resolve the same cases that get_expr_result_type() can, plus one
+ * more: if the expression yields domain-over-composite, it will look through
+ * the domain and return the base type's tupdesc. This is generally *not*
+ * appropriate to use when the caller is responsible for constructing a value
+ * of the expression's type, since one would end up constructing a value of
+ * the base type and failing to apply domain constraints if any. However,
+ * when the goal is just to find out what fields can be extracted from the
+ * expression's value, this is a convenient helper function.
+ */
+ TupleDesc
+ get_expr_result_tupdesc(Node *expr, bool noError)
+ {
+ TupleDesc tupleDesc;
+ TupleDesc cache_tupdesc;
+ Oid exprTypeId;
+ Oid baseTypeId;
+ int32 baseTypeMod;
+
+ /*
+ * First, try get_expr_result_type(), because that can handle references
+ * to functions returning anonymous record types.
+ */
+ if (get_expr_result_type(expr, NULL, &tupleDesc) == TYPEFUNC_COMPOSITE)
+ return tupleDesc;
+
+ /*
+ * There is one case get_expr_result_type() doesn't handle that we should,
+ * which is a domain over composite. So drill through any domain type
+ * before asking lookup_rowtype_tupdesc_noerror().
+ */
+ exprTypeId = exprType(expr);
+ baseTypeMod = exprTypmod(expr);
+ baseTypeId = getBaseTypeAndTypmod(exprTypeId, &baseTypeMod);
+
+ cache_tupdesc = lookup_rowtype_tupdesc_noerror(baseTypeId, baseTypeMod,
+ true);
+ if (cache_tupdesc)
+ {
+ /*
+ * Success! But caller isn't expecting to have to manage a tupdesc
+ * refcount, so copy the cached tupdesc.
+ */
+ tupleDesc = CreateTupleDescCopyConstr(cache_tupdesc);
+ DecrTupleDescRefCount(cache_tupdesc);
+ return tupleDesc;
+ }
+
+ /*
+ * Throw error if requested. We could have left this to
+ * lookup_rowtype_tupdesc_noerror, but if we're dealing with a domain
+ * type, we prefer to finger the domain not its base type.
+ */
+ if (!noError)
+ {
+ if (exprTypeId != RECORDOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("type %s is not composite",
+ format_type_be(exprTypeId))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("record type has not been registered")));
+ }
+
+ return NULL;
+ }
+
+ /*
* Given the result tuple descriptor for a function with OUT parameters,
* replace any polymorphic columns (ANYELEMENT etc) with correct data types
* deduced from the input arguments. Returns TRUE if able to deduce all types,
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 951af2a..0909961 100644
*** a/src/include/funcapi.h
--- b/src/include/funcapi.h
*************** typedef struct FuncCallContext
*** 143,148 ****
--- 143,151 ----
* get_call_result_type. Note: the cases in which rowtypes cannot be
* determined are different from the cases for get_call_result_type.
* Do *not* use this if you can use one of the others.
+ *
+ * A related function, which should generally *not* be used for identifying
+ * a function's result type, is get_expr_result_tupdesc().
*----------
*/
*************** extern TypeFuncClass get_func_result_typ
*** 165,170 ****
--- 168,175 ----
Oid *resultTypeId,
TupleDesc *resultTupleDesc);
+ extern TupleDesc get_expr_result_tupdesc(Node *expr, bool noError);
+
extern bool resolve_polymorphic_argtypes(int numargs, Oid *argtypes,
char *argmodes,
Node *call_expr);
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index 7b843d0..9deec0d 100644
*** a/src/include/parser/parse_type.h
--- b/src/include/parser/parse_type.h
*************** extern Oid typeTypeCollation(Type typ);
*** 46,55 ****
extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
extern Oid typeidTypeRelid(Oid type_id);
extern TypeName *typeStringToTypeName(const char *str);
extern void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok);
! #define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid)
#endif /* PARSE_TYPE_H */
--- 46,56 ----
extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
extern Oid typeidTypeRelid(Oid type_id);
+ extern Oid typeOrDomainTypeRelid(Oid type_id);
extern TypeName *typeStringToTypeName(const char *str);
extern void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok);
! #define ISCOMPLEX(typeid) (typeOrDomainTypeRelid(typeid) != InvalidOid)
#endif /* PARSE_TYPE_H */
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index 5fe999e..21075f5 100644
*** a/src/test/regress/expected/domain.out
--- b/src/test/regress/expected/domain.out
*************** select pg_typeof('{1,2,3}'::dia || 42);
*** 198,203 ****
--- 198,291 ----
(1 row)
drop domain dia;
+ -- Test domains over composites
+ create type comptype as (r float8, i float8);
+ create domain dcomptype as comptype;
+ create table dcomptable (d1 dcomptype unique);
+ insert into dcomptable values (row(1,2)::dcomptype);
+ insert into dcomptable values (row(3,4)::comptype);
+ insert into dcomptable values (row(1,2)::dcomptype); -- fail on uniqueness
+ ERROR: duplicate key value violates unique constraint "dcomptable_d1_key"
+ DETAIL: Key (d1)=((1,2)) already exists.
+ insert into dcomptable (d1.r) values(11);
+ select * from dcomptable;
+ d1
+ -------
+ (1,2)
+ (3,4)
+ (11,)
+ (3 rows)
+
+ select (d1).r, (d1).i, (d1).* from dcomptable;
+ r | i | r | i
+ ----+---+----+---
+ 1 | 2 | 1 | 2
+ 3 | 4 | 3 | 4
+ 11 | | 11 |
+ (3 rows)
+
+ update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0;
+ select * from dcomptable;
+ d1
+ -------
+ (11,)
+ (2,2)
+ (4,4)
+ (3 rows)
+
+ alter domain dcomptype add constraint c1 check ((value).r <= (value).i);
+ alter domain dcomptype add constraint c2 check ((value).r > (value).i); -- fail
+ ERROR: column "d1" of table "dcomptable" contains values that violate the new constraint
+ select row(2,1)::dcomptype; -- fail
+ ERROR: value for domain dcomptype violates check constraint "c1"
+ insert into dcomptable values (row(1,2)::comptype);
+ insert into dcomptable values (row(2,1)::comptype); -- fail
+ ERROR: value for domain dcomptype violates check constraint "c1"
+ insert into dcomptable (d1.r) values(99);
+ insert into dcomptable (d1.r, d1.i) values(99, 100);
+ insert into dcomptable (d1.r, d1.i) values(100, 99); -- fail
+ ERROR: value for domain dcomptype violates check constraint "c1"
+ update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0; -- fail
+ ERROR: value for domain dcomptype violates check constraint "c1"
+ update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
+ select * from dcomptable;
+ d1
+ ----------
+ (11,)
+ (99,)
+ (1,3)
+ (3,5)
+ (0,3)
+ (98,101)
+ (6 rows)
+
+ explain (verbose, costs off)
+ update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
+ QUERY PLAN
+ -----------------------------------------------------------------------------------------------
+ Update on public.dcomptable
+ -> Seq Scan on public.dcomptable
+ Output: ROW(((d1).r - '1'::double precision), ((d1).i + '1'::double precision)), ctid
+ Filter: ((dcomptable.d1).i > '0'::double precision)
+ (4 rows)
+
+ create rule silly as on delete to dcomptable do instead
+ update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
+ \d+ dcomptable
+ Table "public.dcomptable"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+ --------+-----------+-----------+----------+---------+----------+--------------+-------------
+ d1 | dcomptype | | | | extended | |
+ Indexes:
+ "dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
+ Rules:
+ silly AS
+ ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1.r = (dcomptable.d1).r - 1::double precision, d1.i = (dcomptable.d1).i + 1::double precision
+ WHERE (dcomptable.d1).i > 0::double precision
+
+ drop table dcomptable;
+ drop type comptype cascade;
+ NOTICE: drop cascades to type dcomptype
-- Test domains over arrays of composite
create type comptype as (r float8, i float8);
create domain dcomptypea as comptype[];
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index 5ec128d..ba45165 100644
*** a/src/test/regress/sql/domain.sql
--- b/src/test/regress/sql/domain.sql
*************** select pg_typeof('{1,2,3}'::dia || 42);
*** 120,125 ****
--- 120,164 ----
drop domain dia;
+ -- Test domains over composites
+
+ create type comptype as (r float8, i float8);
+ create domain dcomptype as comptype;
+ create table dcomptable (d1 dcomptype unique);
+
+ insert into dcomptable values (row(1,2)::dcomptype);
+ insert into dcomptable values (row(3,4)::comptype);
+ insert into dcomptable values (row(1,2)::dcomptype); -- fail on uniqueness
+ insert into dcomptable (d1.r) values(11);
+
+ select * from dcomptable;
+ select (d1).r, (d1).i, (d1).* from dcomptable;
+ update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0;
+ select * from dcomptable;
+
+ alter domain dcomptype add constraint c1 check ((value).r <= (value).i);
+ alter domain dcomptype add constraint c2 check ((value).r > (value).i); -- fail
+
+ select row(2,1)::dcomptype; -- fail
+ insert into dcomptable values (row(1,2)::comptype);
+ insert into dcomptable values (row(2,1)::comptype); -- fail
+ insert into dcomptable (d1.r) values(99);
+ insert into dcomptable (d1.r, d1.i) values(99, 100);
+ insert into dcomptable (d1.r, d1.i) values(100, 99); -- fail
+ update dcomptable set d1.r = (d1).r + 1 where (d1).i > 0; -- fail
+ update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
+ select * from dcomptable;
+
+ explain (verbose, costs off)
+ update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
+ create rule silly as on delete to dcomptable do instead
+ update dcomptable set d1.r = (d1).r - 1, d1.i = (d1).i + 1 where (d1).i > 0;
+ \d+ dcomptable
+
+ drop table dcomptable;
+ drop type comptype cascade;
+
+
-- Test domains over arrays of composite
create type comptype as (r float8, i float8);