Commit 967a7b0f authored by Tom Lane's avatar Tom Lane

Avoid reporting "cache lookup failed" for some user-reachable cases.

We have a not-terribly-thoroughly-enforced-yet project policy that internal
errors with SQLSTATE XX000 (ie, plain elog) should not be triggerable from
SQL.  record_in, domain_in, and PL validator functions all failed to meet
this standard, because they threw plain elog("cache lookup failed for XXX")
errors on bad OIDs, and those are all invokable from SQL.

For record_in, the best fix is to upgrade typcache.c (lookup_type_cache)
to throw a user-facing error for this case.  That seems consistent because
it was more than halfway there already, having user-facing errors for shell
types and non-composite types.  Having done that, tweak domain_in to rely
on the typcache to throw an appropriate error.  (This costs little because
InitDomainConstraintRef would fetch the typcache entry anyway.)

For the PL validator functions, we already have a single choke point at
CheckFunctionValidatorAccess, so just fix its error to be user-facing.

Dilip Kumar, reviewed by Haribabu Kommi

Discussion: <87wpxfygg9.fsf@credativ.de>
parent ec253de1
...@@ -72,19 +72,28 @@ static DomainIOData * ...@@ -72,19 +72,28 @@ static DomainIOData *
domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt) domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
{ {
DomainIOData *my_extra; DomainIOData *my_extra;
TypeCacheEntry *typentry;
Oid baseType; Oid baseType;
my_extra = (DomainIOData *) MemoryContextAlloc(mcxt, sizeof(DomainIOData)); my_extra = (DomainIOData *) MemoryContextAlloc(mcxt, sizeof(DomainIOData));
/* Find out the base type */ /*
my_extra->typtypmod = -1; * Verify that domainType represents a valid domain type. We need to be
baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod); * careful here because domain_in and domain_recv can be called from SQL,
if (baseType == domainType) * possibly with incorrect arguments. We use lookup_type_cache mainly
* because it will throw a clean user-facing error for a bad OID.
*/
typentry = lookup_type_cache(domainType, 0);
if (typentry->typtype != TYPTYPE_DOMAIN)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type %s is not a domain", errmsg("type %s is not a domain",
format_type_be(domainType)))); format_type_be(domainType))));
/* Find out the base type */
my_extra->typtypmod = -1;
baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
/* Look up underlying I/O function */ /* Look up underlying I/O function */
if (binary) if (binary)
getTypeBinaryInputInfo(baseType, getTypeBinaryInputInfo(baseType,
......
...@@ -182,10 +182,10 @@ static int enum_oid_cmp(const void *left, const void *right); ...@@ -182,10 +182,10 @@ static int enum_oid_cmp(const void *left, const void *right);
* Fetch the type cache entry for the specified datatype, and make sure that * Fetch the type cache entry for the specified datatype, and make sure that
* all the fields requested by bits in 'flags' are valid. * all the fields requested by bits in 'flags' are valid.
* *
* The result is never NULL --- we will elog() if the passed type OID is * The result is never NULL --- we will ereport() if the passed type OID is
* invalid. Note however that we may fail to find one or more of the * invalid. Note however that we may fail to find one or more of the
* requested opclass-dependent fields; the caller needs to check whether * values requested by 'flags'; the caller needs to check whether the fields
* the fields are InvalidOid or not. * are InvalidOid or not.
*/ */
TypeCacheEntry * TypeCacheEntry *
lookup_type_cache(Oid type_id, int flags) lookup_type_cache(Oid type_id, int flags)
...@@ -224,14 +224,18 @@ lookup_type_cache(Oid type_id, int flags) ...@@ -224,14 +224,18 @@ lookup_type_cache(Oid type_id, int flags)
/* /*
* If we didn't find one, we want to make one. But first look up the * If we didn't find one, we want to make one. But first look up the
* pg_type row, just to make sure we don't make a cache entry for an * pg_type row, just to make sure we don't make a cache entry for an
* invalid type OID. * invalid type OID. If the type OID is not valid, present a
* user-facing error, since some code paths such as domain_in() allow
* this function to be reached with a user-supplied OID.
*/ */
HeapTuple tp; HeapTuple tp;
Form_pg_type typtup; Form_pg_type typtup;
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id)); tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
if (!HeapTupleIsValid(tp)) if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for type %u", type_id); ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type with OID %u does not exist", type_id)));
typtup = (Form_pg_type) GETSTRUCT(tp); typtup = (Form_pg_type) GETSTRUCT(tp);
if (!typtup->typisdefined) if (!typtup->typisdefined)
ereport(ERROR, ereport(ERROR,
...@@ -1230,6 +1234,8 @@ lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError) ...@@ -1230,6 +1234,8 @@ lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
* *
* Given a typeid/typmod that should describe a known composite type, * Given a typeid/typmod that should describe a known composite type,
* return the tuple descriptor for the type. Will ereport on failure. * return the tuple descriptor for the type. Will ereport on failure.
* (Use ereport because this is reachable with user-specified OIDs,
* for example from record_in().)
* *
* Note: on success, we increment the refcount of the returned TupleDesc, * Note: on success, we increment the refcount of the returned TupleDesc,
* and log the reference in CurrentResourceOwner. Caller should call * and log the reference in CurrentResourceOwner. Caller should call
......
...@@ -2455,10 +2455,15 @@ CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid) ...@@ -2455,10 +2455,15 @@ CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid)
Form_pg_language langStruct; Form_pg_language langStruct;
AclResult aclresult; AclResult aclresult;
/* Get the function's pg_proc entry */ /*
* Get the function's pg_proc entry. Throw a user-facing error for bad
* OID, because validators can be called with user-specified OIDs.
*/
procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid)); procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));
if (!HeapTupleIsValid(procTup)) if (!HeapTupleIsValid(procTup))
elog(ERROR, "cache lookup failed for function %u", functionOid); ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function with OID %u does not exist", functionOid)));
procStruct = (Form_pg_proc) GETSTRUCT(procTup); procStruct = (Form_pg_proc) GETSTRUCT(procTup);
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment