mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Support domains over composite types.
This is the last major omission in our domains feature: you can now make a domain over anything that's not a pseudotype. The major complication from an implementation standpoint is that places that might be creating tuples of a domain type now need to be prepared to apply domain_check(). It seems better that unprepared code fail with an error like "<type> is not composite" than that it silently fail to apply domain constraints. Therefore, relevant infrastructure like get_func_result_type() and lookup_rowtype_tupdesc() has been adjusted to treat domain-over-composite as a distinct case that unprepared code won't recognize, rather than just transparently treating it the same as plain composite. This isn't a 100% solution to the possibility of overlooked domain checks, but it catches most places. In passing, improve typcache.c's support for domains (it can now cache the identity of a domain's base type), and rewrite the argument handling logic in jsonfuncs.c's populate_record[set]_worker to reduce duplicative per-call lookups. I believe this is code-complete so far as the core and contrib code go. The PLs need varying amounts of work, which will be tackled in followup patches. Discussion: https://postgr.es/m/4206.1499798337@sss.pgh.pa.us
This commit is contained in:
18
src/backend/utils/cache/lsyscache.c
vendored
18
src/backend/utils/cache/lsyscache.c
vendored
@ -2398,12 +2398,26 @@ get_typtype(Oid typid)
|
||||
* type_is_rowtype
|
||||
*
|
||||
* Convenience function to determine whether a type OID represents
|
||||
* a "rowtype" type --- either RECORD or a named composite type.
|
||||
* a "rowtype" type --- either RECORD or a named composite type
|
||||
* (including a domain over a named composite type).
|
||||
*/
|
||||
bool
|
||||
type_is_rowtype(Oid typid)
|
||||
{
|
||||
return (typid == RECORDOID || get_typtype(typid) == TYPTYPE_COMPOSITE);
|
||||
if (typid == RECORDOID)
|
||||
return true; /* easy case */
|
||||
switch (get_typtype(typid))
|
||||
{
|
||||
case TYPTYPE_COMPOSITE:
|
||||
return true;
|
||||
case TYPTYPE_DOMAIN:
|
||||
if (get_typtype(getBaseType(typid)) == TYPTYPE_COMPOSITE)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
130
src/backend/utils/cache/typcache.c
vendored
130
src/backend/utils/cache/typcache.c
vendored
@ -96,6 +96,7 @@ static TypeCacheEntry *firstDomainTypeEntry = NULL;
|
||||
#define TCFLAGS_HAVE_FIELD_EQUALITY 0x004000
|
||||
#define TCFLAGS_HAVE_FIELD_COMPARE 0x008000
|
||||
#define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS 0x010000
|
||||
#define TCFLAGS_DOMAIN_BASE_IS_COMPOSITE 0x020000
|
||||
|
||||
/*
|
||||
* Data stored about a domain type's constraints. Note that we do not create
|
||||
@ -747,7 +748,15 @@ lookup_type_cache(Oid type_id, int flags)
|
||||
/*
|
||||
* If requested, get information about a domain type
|
||||
*/
|
||||
if ((flags & TYPECACHE_DOMAIN_INFO) &&
|
||||
if ((flags & TYPECACHE_DOMAIN_BASE_INFO) &&
|
||||
typentry->domainBaseType == InvalidOid &&
|
||||
typentry->typtype == TYPTYPE_DOMAIN)
|
||||
{
|
||||
typentry->domainBaseTypmod = -1;
|
||||
typentry->domainBaseType =
|
||||
getBaseTypeAndTypmod(type_id, &typentry->domainBaseTypmod);
|
||||
}
|
||||
if ((flags & TYPECACHE_DOMAIN_CONSTR_INFO) &&
|
||||
(typentry->flags & TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS) == 0 &&
|
||||
typentry->typtype == TYPTYPE_DOMAIN)
|
||||
{
|
||||
@ -1166,7 +1175,7 @@ InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
|
||||
MemoryContext refctx, bool need_exprstate)
|
||||
{
|
||||
/* Look up the typcache entry --- we assume it survives indefinitely */
|
||||
ref->tcache = lookup_type_cache(type_id, TYPECACHE_DOMAIN_INFO);
|
||||
ref->tcache = lookup_type_cache(type_id, TYPECACHE_DOMAIN_CONSTR_INFO);
|
||||
ref->need_exprstate = need_exprstate;
|
||||
/* For safety, establish the callback before acquiring a refcount */
|
||||
ref->refctx = refctx;
|
||||
@ -1257,7 +1266,7 @@ DomainHasConstraints(Oid type_id)
|
||||
* Note: a side effect is to cause the typcache's domain data to become
|
||||
* valid. This is fine since we'll likely need it soon if there is any.
|
||||
*/
|
||||
typentry = lookup_type_cache(type_id, TYPECACHE_DOMAIN_INFO);
|
||||
typentry = lookup_type_cache(type_id, TYPECACHE_DOMAIN_CONSTR_INFO);
|
||||
|
||||
return (typentry->domainData != NULL);
|
||||
}
|
||||
@ -1405,6 +1414,29 @@ cache_record_field_properties(TypeCacheEntry *typentry)
|
||||
|
||||
DecrTupleDescRefCount(tupdesc);
|
||||
}
|
||||
else if (typentry->typtype == TYPTYPE_DOMAIN)
|
||||
{
|
||||
/* If it's domain over composite, copy base type's properties */
|
||||
TypeCacheEntry *baseentry;
|
||||
|
||||
/* load up basetype info if we didn't already */
|
||||
if (typentry->domainBaseType == InvalidOid)
|
||||
{
|
||||
typentry->domainBaseTypmod = -1;
|
||||
typentry->domainBaseType =
|
||||
getBaseTypeAndTypmod(typentry->type_id,
|
||||
&typentry->domainBaseTypmod);
|
||||
}
|
||||
baseentry = lookup_type_cache(typentry->domainBaseType,
|
||||
TYPECACHE_EQ_OPR |
|
||||
TYPECACHE_CMP_PROC);
|
||||
if (baseentry->typtype == TYPTYPE_COMPOSITE)
|
||||
{
|
||||
typentry->flags |= TCFLAGS_DOMAIN_BASE_IS_COMPOSITE;
|
||||
typentry->flags |= baseentry->flags & (TCFLAGS_HAVE_FIELD_EQUALITY |
|
||||
TCFLAGS_HAVE_FIELD_COMPARE);
|
||||
}
|
||||
}
|
||||
typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
|
||||
}
|
||||
|
||||
@ -1618,6 +1650,53 @@ lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
|
||||
return CreateTupleDescCopyConstr(tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* lookup_rowtype_tupdesc_domain
|
||||
*
|
||||
* Same as lookup_rowtype_tupdesc_noerror(), except that the type can also be
|
||||
* a domain over a named composite type; so this is effectively equivalent to
|
||||
* lookup_rowtype_tupdesc_noerror(getBaseType(type_id), typmod, noError)
|
||||
* except for being a tad faster.
|
||||
*
|
||||
* Note: the reason we don't fold the look-through-domain behavior into plain
|
||||
* lookup_rowtype_tupdesc() is that we want callers to know they might be
|
||||
* dealing with a domain. Otherwise they might construct a tuple that should
|
||||
* be of the domain type, but not apply domain constraints.
|
||||
*/
|
||||
TupleDesc
|
||||
lookup_rowtype_tupdesc_domain(Oid type_id, int32 typmod, bool noError)
|
||||
{
|
||||
TupleDesc tupDesc;
|
||||
|
||||
if (type_id != RECORDOID)
|
||||
{
|
||||
/*
|
||||
* Check for domain or named composite type. We might as well load
|
||||
* whichever data is needed.
|
||||
*/
|
||||
TypeCacheEntry *typentry;
|
||||
|
||||
typentry = lookup_type_cache(type_id,
|
||||
TYPECACHE_TUPDESC |
|
||||
TYPECACHE_DOMAIN_BASE_INFO);
|
||||
if (typentry->typtype == TYPTYPE_DOMAIN)
|
||||
return lookup_rowtype_tupdesc_noerror(typentry->domainBaseType,
|
||||
typentry->domainBaseTypmod,
|
||||
noError);
|
||||
if (typentry->tupDesc == NULL && !noError)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("type %s is not composite",
|
||||
format_type_be(type_id))));
|
||||
tupDesc = typentry->tupDesc;
|
||||
}
|
||||
else
|
||||
tupDesc = lookup_rowtype_tupdesc_internal(type_id, typmod, noError);
|
||||
if (tupDesc != NULL)
|
||||
PinTupleDesc(tupDesc);
|
||||
return tupDesc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash function for the hash table of RecordCacheEntry.
|
||||
*/
|
||||
@ -1929,29 +2008,40 @@ TypeCacheRelCallback(Datum arg, Oid relid)
|
||||
hash_seq_init(&status, TypeCacheHash);
|
||||
while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
|
||||
{
|
||||
if (typentry->typtype != TYPTYPE_COMPOSITE)
|
||||
continue; /* skip non-composites */
|
||||
if (typentry->typtype == TYPTYPE_COMPOSITE)
|
||||
{
|
||||
/* Skip if no match, unless we're zapping all composite types */
|
||||
if (relid != typentry->typrelid && relid != InvalidOid)
|
||||
continue;
|
||||
|
||||
/* Skip if no match, unless we're zapping all composite types */
|
||||
if (relid != typentry->typrelid && relid != InvalidOid)
|
||||
continue;
|
||||
/* Delete tupdesc if we have it */
|
||||
if (typentry->tupDesc != NULL)
|
||||
{
|
||||
/*
|
||||
* Release our refcount, and free the tupdesc if none remain.
|
||||
* (Can't use DecrTupleDescRefCount because this reference is
|
||||
* not logged in current resource owner.)
|
||||
*/
|
||||
Assert(typentry->tupDesc->tdrefcount > 0);
|
||||
if (--typentry->tupDesc->tdrefcount == 0)
|
||||
FreeTupleDesc(typentry->tupDesc);
|
||||
typentry->tupDesc = NULL;
|
||||
}
|
||||
|
||||
/* Delete tupdesc if we have it */
|
||||
if (typentry->tupDesc != NULL)
|
||||
/* Reset equality/comparison/hashing validity information */
|
||||
typentry->flags = 0;
|
||||
}
|
||||
else if (typentry->typtype == TYPTYPE_DOMAIN)
|
||||
{
|
||||
/*
|
||||
* Release our refcount, and free the tupdesc if none remain.
|
||||
* (Can't use DecrTupleDescRefCount because this reference is not
|
||||
* logged in current resource owner.)
|
||||
* If it's domain over composite, reset flags. (We don't bother
|
||||
* trying to determine whether the specific base type needs a
|
||||
* reset.) Note that if we haven't determined whether the base
|
||||
* type is composite, we don't need to reset anything.
|
||||
*/
|
||||
Assert(typentry->tupDesc->tdrefcount > 0);
|
||||
if (--typentry->tupDesc->tdrefcount == 0)
|
||||
FreeTupleDesc(typentry->tupDesc);
|
||||
typentry->tupDesc = NULL;
|
||||
if (typentry->flags & TCFLAGS_DOMAIN_BASE_IS_COMPOSITE)
|
||||
typentry->flags = 0;
|
||||
}
|
||||
|
||||
/* Reset equality/comparison/hashing validity information */
|
||||
typentry->flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user