diff options
Diffstat (limited to 'src/backend/access/brin/brin_validate.c')
-rw-r--r-- | src/backend/access/brin/brin_validate.c | 260 |
1 files changed, 193 insertions, 67 deletions
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c index 956ecb95a01..4cb1bca8e45 100644 --- a/src/backend/access/brin/brin_validate.c +++ b/src/backend/access/brin/brin_validate.c @@ -13,31 +13,46 @@ */ #include "postgres.h" +#include "access/amvalidate.h" #include "access/brin_internal.h" #include "access/htup_details.h" #include "catalog/pg_amop.h" #include "catalog/pg_amproc.h" #include "catalog/pg_opclass.h" -#include "utils/catcache.h" +#include "catalog/pg_opfamily.h" +#include "catalog/pg_type.h" +#include "utils/builtins.h" #include "utils/syscache.h" /* * Validator for a BRIN opclass. + * + * Some of the checks done here cover the whole opfamily, and therefore are + * redundant when checking each opclass in a family. But they don't run long + * enough to be much of a problem, so we accept the duplication rather than + * complicate the amvalidate API. */ bool brinvalidate(Oid opclassoid) { + bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; - int numclassops; - int32 classfuncbits; + char *opclassname; + HeapTuple familytup; + Form_pg_opfamily familyform; + char *opfamilyname; CatCList *proclist, *oprlist; - int i, - j; + uint64 allfuncs = 0; + uint64 allops = 0; + List *grouplist; + OpFamilyOpFuncGroup *opclassgroup; + int i; + ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); @@ -47,106 +62,217 @@ brinvalidate(Oid opclassoid) opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; + opclassname = NameStr(classform->opcname); - ReleaseSysCache(classtup); + /* Fetch opfamily information */ + familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid)); + if (!HeapTupleIsValid(familytup)) + elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid); + familyform = (Form_pg_opfamily) GETSTRUCT(familytup); + + opfamilyname = NameStr(familyform->opfname); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); - /* We'll track the ops and functions belonging to the named opclass */ - numclassops = 0; - classfuncbits = 0; - - /* Check support functions */ + /* Check individual support functions */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); + bool ok; - /* Check that only allowed procedure numbers exist */ - if (procform->amprocnum < 1 || - procform->amprocnum > BRIN_LAST_OPTIONAL_PROCNUM) - ereport(ERROR, + /* Check procedure numbers and function signatures */ + switch (procform->amprocnum) + { + case BRIN_PROCNUM_OPCINFO: + ok = check_amproc_signature(procform->amproc, INTERNALOID, true, + 1, 1, INTERNALOID); + break; + case BRIN_PROCNUM_ADDVALUE: + ok = check_amproc_signature(procform->amproc, BOOLOID, true, + 4, 4, INTERNALOID, INTERNALOID, + INTERNALOID, INTERNALOID); + break; + case BRIN_PROCNUM_CONSISTENT: + ok = check_amproc_signature(procform->amproc, BOOLOID, true, + 3, 3, INTERNALOID, INTERNALOID, + INTERNALOID); + break; + case BRIN_PROCNUM_UNION: + ok = check_amproc_signature(procform->amproc, BOOLOID, true, + 3, 3, INTERNALOID, INTERNALOID, + INTERNALOID); + break; + default: + /* Complain if it's not a valid optional proc number */ + if (procform->amprocnum < BRIN_FIRST_OPTIONAL_PROCNUM || + procform->amprocnum > BRIN_LAST_OPTIONAL_PROCNUM) + { + ereport(INFO, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("brin opfamily %s contains function %s with invalid support number %d", + opfamilyname, + format_procedure(procform->amproc), + procform->amprocnum))); + result = false; + continue; /* omit bad proc numbers from allfuncs */ + } + /* Can't check signatures of optional procs, so assume OK */ + ok = true; + break; + } + + if (!ok) + { + ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("brin opfamily %u contains invalid support number %d for procedure %u", - opfamilyoid, - procform->amprocnum, procform->amproc))); - - /* Remember functions that are specifically for the named opclass */ - if (procform->amproclefttype == opcintype && - procform->amprocrighttype == opcintype) - classfuncbits |= (1 << procform->amprocnum); + errmsg("brin opfamily %s contains function %s with wrong signature for support number %d", + opfamilyname, + format_procedure(procform->amproc), + procform->amprocnum))); + result = false; + } + + /* Track all valid procedure numbers seen in opfamily */ + allfuncs |= ((uint64) 1) << procform->amprocnum; } - /* Check operators */ + /* Check individual operators */ for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); - bool found = false; - /* TODO: Check that only allowed strategy numbers exist */ - if (oprform->amopstrategy < 1) - ereport(ERROR, + /* Check that only allowed strategy numbers exist */ + if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63) + { + ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("brin opfamily %u contains invalid strategy number %d for operator %u", - opfamilyoid, - oprform->amopstrategy, oprform->amopopr))); - - /* TODO: check more thoroughly for missing support functions */ - for (j = 0; j < proclist->n_members; j++) + errmsg("brin opfamily %s contains operator %s with invalid strategy number %d", + opfamilyname, + format_operator(oprform->amopopr), + oprform->amopstrategy))); + result = false; + } + else { - HeapTuple proctup = &proclist->members[j]->tuple; - Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); - - /* note only the operator's lefttype matters */ - if (procform->amproclefttype == oprform->amoplefttype && - procform->amprocrighttype == oprform->amoplefttype) - { - found = true; - break; - } + /* + * The set of operators supplied varies across BRIN opfamilies. + * Our plan is to identify all operator strategy numbers used in + * the opfamily and then complain about datatype combinations that + * are missing any operator(s). However, consider only numbers + * that appear in some non-cross-type case, since cross-type + * operators may have unique strategies. (This is not a great + * heuristic, in particular an erroneous number used in a + * cross-type operator will not get noticed; but the core BRIN + * opfamilies are messy enough to make it necessary.) + */ + if (oprform->amoplefttype == oprform->amoprighttype) + allops |= ((uint64) 1) << oprform->amopstrategy; } - if (!found) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("brin opfamily %u lacks support function for operator %u", - opfamilyoid, oprform->amopopr))); - /* brin doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) - ereport(ERROR, + { + ereport(INFO, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("brin opfamily %s contains invalid ORDER BY specification for operator %s", + opfamilyname, + format_operator(oprform->amopopr)))); + result = false; + } + + /* Check operator signature --- same for all brin strategies */ + if (!check_amop_signature(oprform->amopopr, BOOLOID, + oprform->amoplefttype, + oprform->amoprighttype)) + { + ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("brin opfamily %u contains invalid ORDER BY specification for operator %u", - opfamilyoid, oprform->amopopr))); + errmsg("brin opfamily %s contains operator %s with wrong signature", + opfamilyname, + format_operator(oprform->amopopr)))); + result = false; + } + } + + /* Now check for inconsistent groups of operators/functions */ + grouplist = identify_opfamily_groups(oprlist, proclist); + opclassgroup = NULL; + foreach(lc, grouplist) + { + OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc); + + /* Remember the group exactly matching the test opclass */ + if (thisgroup->lefttype == opcintype && + thisgroup->righttype == opcintype) + opclassgroup = thisgroup; - /* Count operators that are specifically for the named opclass */ - if (oprform->amoplefttype == opcintype && - oprform->amoprighttype == opcintype) - numclassops++; + /* + * Some BRIN opfamilies expect cross-type support functions to exist, + * and some don't. We don't know exactly which are which, so if we + * find a cross-type operator for which there are no support functions + * at all, let it pass. (Don't expect that all operators exist for + * such cross-type cases, either.) + */ + if (thisgroup->functionset == 0 && + thisgroup->lefttype != thisgroup->righttype) + continue; + + /* + * Else complain if there seems to be an incomplete set of either + * operators or support functions for this datatype pair. + */ + if (thisgroup->operatorset != allops) + { + ereport(INFO, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("brin opfamily %s is missing operator(s) for types %s and %s", + opfamilyname, + format_type_be(thisgroup->lefttype), + format_type_be(thisgroup->righttype)))); + result = false; + } + if (thisgroup->functionset != allfuncs) + { + ereport(INFO, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("brin opfamily %s is missing support function(s) for types %s and %s", + opfamilyname, + format_type_be(thisgroup->lefttype), + format_type_be(thisgroup->righttype)))); + result = false; + } } - /* Check that the named opclass is complete */ - if (numclassops == 0) - ereport(ERROR, + /* Check that the originally-named opclass is complete */ + if (!opclassgroup || opclassgroup->operatorset != allops) + { + ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("brin opclass %u is missing operator(s)", - opclassoid))); + errmsg("brin opclass %s is missing operator(s)", + opclassname))); + result = false; + } for (i = 1; i <= BRIN_MANDATORY_NPROCS; i++) { - if ((classfuncbits & (1 << i)) != 0) + if (opclassgroup && + (opclassgroup->functionset & (((int64) 1) << i)) != 0) continue; /* got it */ - ereport(ERROR, + ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("brin opclass %u is missing required support function %d", - opclassoid, i))); + errmsg("brin opclass %s is missing support function %d", + opclassname, i))); + result = false; } ReleaseCatCacheList(proclist); ReleaseCatCacheList(oprlist); + ReleaseSysCache(familytup); + ReleaseSysCache(classtup); - return true; + return result; } |