/* * Is the datatype a legitimate input type for the btree opfamily? */ bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid) { bool result = false; CatCList *opclist; int i; /* * We search through all btree opclasses to see if one matches. This is a * bit inefficient but there is no better index available. It also saves * making an explicit check that the opfamily belongs to btree. */ opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(BTREE_AM_OID)); for (i = 0; i < opclist->n_members; i++) { HeapTuple classtup = &opclist->members[i]->tuple; Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup); if (classform->opcfamily == opfamilyoid && classform->opcintype == datatypeoid) { result = true; break; } } ReleaseCatCacheList(opclist); return result; }
/* * Gets list of publication oids for a relation oid. */ List * GetRelationPublications(Oid relid) { List *result = NIL; CatCList *pubrellist; int i; /* Find all publications associated with the relation. */ pubrellist = SearchSysCacheList1(PUBLICATIONRELMAP, ObjectIdGetDatum(relid)); for (i = 0; i < pubrellist->n_members; i++) { HeapTuple tup = &pubrellist->members[i]->tuple; Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid; result = lappend_oid(result, pubid); } ReleaseSysCacheList(pubrellist); return result; }
/* * Validator for a btree 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 btvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; List *familytypes; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* 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)); /* 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 procedure numbers and function signatures */ switch (procform->amprocnum) { case BTORDER_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, true, 2, 2, procform->amproclefttype, procform->amprocrighttype); break; case BTSORTSUPPORT_PROC: ok = check_amproc_signature(procform->amproc, VOIDOID, true, 1, 1, INTERNALOID); break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* don't want additional message */ } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } } /* 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); /* Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > BTMaxStrategyNumber) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* btree doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all btree strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%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; familytypes = NIL; 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; /* * Identify all distinct data types handled in this opfamily. This * implementation is O(N^2), but there aren't likely to be enough * types in the family for it to matter. */ familytypes = list_append_unique_oid(familytypes, thisgroup->lefttype); familytypes = list_append_unique_oid(familytypes, thisgroup->righttype); /* * Complain if there seems to be an incomplete set of either operators * or support functions for this datatype pair. The only thing that * is considered optional is the sortsupport function. */ if (thisgroup->operatorset != ((1 << BTLessStrategyNumber) | (1 << BTLessEqualStrategyNumber) | (1 << BTEqualStrategyNumber) | (1 << BTGreaterEqualStrategyNumber) | (1 << BTGreaterStrategyNumber))) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%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 & (1 << BTORDER_PROC)) == 0) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree operator family \"%s\" is missing support function for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } }
/* * Validator for a hash 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 hashvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; List *hashabletypes = NIL; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* 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)); /* 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); /* * All hash functions should be registered with matching left/right * types */ if (procform->amproclefttype != procform->amprocrighttype) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains support procedure %s with cross-type registration", opfamilyname, format_procedure(procform->amproc)))); result = false; } /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case HASHPROC: if (!check_hash_func_signature(procform->amproc, INT4OID, procform->amproclefttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } else { /* Remember which types we can hash */ hashabletypes = list_append_unique_oid(hashabletypes, procform->amproclefttype); } break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; break; } } /* 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); /* Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > HTMaxStrategyNumber) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* hash doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all hash strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" contains operator %s with wrong signature", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* There should be relevant hash procedures for each datatype */ if (!list_member_oid(hashabletypes, oprform->amoplefttype) || !list_member_oid(hashabletypes, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" lacks support function for operator %s", 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; /* * Complain if there seems to be an incomplete set of operators for * this datatype pair (implying that we have a hash function but no * operator). */ if (thisgroup->operatorset != (1 << HTEqualStrategyNumber)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("hash operator family \"%s\" is missing operator(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } }
/* * sepgsql_relation_drop * * It checks privileges to drop the supplied relation. */ void sepgsql_relation_drop(Oid relOid) { ObjectAddress object; char *audit_name; uint16_t tclass; char relkind; relkind = get_rel_relkind(relOid); switch (relkind) { case RELKIND_RELATION: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: tclass = SEPG_CLASS_DB_SEQUENCE; break; case RELKIND_VIEW: tclass = SEPG_CLASS_DB_VIEW; break; case RELKIND_INDEX: /* ignore indexes on toast tables */ if (get_rel_namespace(relOid) == PG_TOAST_NAMESPACE) return; /* other indexes are handled specially below; no need for tclass */ break; default: /* ignore other relkinds */ return; } /* * check db_schema:{remove_name} permission */ object.classId = NamespaceRelationId; object.objectId = get_rel_namespace(relOid); object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__REMOVE_NAME, audit_name, true); pfree(audit_name); /* deal with indexes specially */ if (relkind == RELKIND_INDEX) { sepgsql_index_modify(relOid); return; } /* * check db_table/sequence/view:{drop} permission */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__DROP, audit_name, true); pfree(audit_name); /* * check db_column:{drop} permission */ if (relkind == RELKIND_RELATION) { Form_pg_attribute attForm; CatCList *attrList; HeapTuple atttup; int i; attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid)); for (i = 0; i < attrList->n_members; i++) { atttup = &attrList->members[i]->tuple; attForm = (Form_pg_attribute) GETSTRUCT(atttup); if (attForm->attisdropped) continue; object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attForm->attnum; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__DROP, audit_name, true); pfree(audit_name); } ReleaseCatCacheList(attrList); } }
/* * Validator for a bloom opclass. */ bool blvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; Oid opckeytype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opckeytype = classform->opckeytype; if (!OidIsValid(opckeytype)) opckeytype = opcintype; opclassname = NameStr(classform->opcname); /* 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)); /* 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; /* * All bloom support functions should be registered with matching * left/right types */ if (procform->amproclefttype != procform->amprocrighttype) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains support procedure %s with cross-type registration", opfamilyname, format_procedure(procform->amproc)))); result = false; } /* * We can't check signatures except within the specific opclass, since * we need to know the associated opckeytype in many cases. */ if (procform->amproclefttype != opcintype) continue; /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case BLOOM_HASH_PROC: ok = check_amproc_signature(procform->amproc, INT4OID, false, 1, 1, opckeytype); break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* don't want additional message */ } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("gist opfamily %s contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } } /* 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); /* Check it's allowed strategy for bloom */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > BLOOM_NSTRATEGIES) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* bloom doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all bloom strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("bloom 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; /* * There is not a lot we can do to check the operator sets, since each * bloom opclass is more or less a law unto itself, and some contain * only operators that are binary-compatible with the opclass datatype * (meaning that empty operator sets can be OK). That case also means * that we shouldn't insist on nonempty function sets except for the * opclass's own group. */ }
/* * Validator for an SP-GiST 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 spgvalidate(Oid opclassoid) { bool result = true; HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* 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)); /* 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; /* * All SP-GiST support functions should be registered with matching * left/right types */ if (procform->amproclefttype != procform->amprocrighttype) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains support procedure %s with cross-type registration", opfamilyname, format_procedure(procform->amproc)))); result = false; } /* Check procedure numbers and function signatures */ switch (procform->amprocnum) { case SPGIST_CONFIG_PROC: case SPGIST_CHOOSE_PROC: case SPGIST_PICKSPLIT_PROC: case SPGIST_INNER_CONSISTENT_PROC: ok = check_amproc_signature(procform->amproc, VOIDOID, true, 2, 2, INTERNALOID, INTERNALOID); break; case SPGIST_LEAF_CONSISTENT_PROC: ok = check_amproc_signature(procform->amproc, BOOLOID, true, 2, 2, INTERNALOID, INTERNALOID); break; default: ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains function %s with invalid support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; continue; /* don't want additional message */ } if (!ok) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains function %s with wrong signature for support number %d", opfamilyname, format_procedure(procform->amproc), procform->amprocnum))); result = false; } } /* 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); /* TODO: Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } /* spgist doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s contains invalid ORDER BY specification for operator %s", opfamilyname, format_operator(oprform->amopopr)))); result = false; } /* Check operator signature --- same for all spgist strategies */ if (!check_amop_signature(oprform->amopopr, BOOLOID, oprform->amoplefttype, oprform->amoprighttype)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist 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; /* * Complain if there are any datatype pairs with functions but no * operators. This is about the best we can do for now to detect * missing operators. */ if (thisgroup->operatorset == 0) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s is missing operator(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } /* * Complain if we're missing functions for any datatype, remembering * that SP-GiST doesn't use cross-type support functions. */ if (thisgroup->lefttype != thisgroup->righttype) continue; for (i = 1; i <= SPGISTNProc; i++) { if ((thisgroup->functionset & (((uint64) 1) << i)) != 0) continue; /* got it */ ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("spgist opfamily %s is missing support function %d for type %s", opfamilyname, i, format_type_be(thisgroup->lefttype)))); result = false; } }
/* * Validator for a BRIN opclass. */ bool brinvalidate(Oid opclassoid) { HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; int numclassops; int32 classfuncbits; CatCList *proclist, *oprlist; int i, j; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; ReleaseSysCache(classtup); /* 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 */ for (i = 0; i < proclist->n_members; i++) { HeapTuple proctup = &proclist->members[i]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); /* Check that only allowed procedure numbers exist */ if (procform->amprocnum < 1 || procform->amprocnum > BRIN_LAST_OPTIONAL_PROCNUM) ereport(ERROR, (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); } /* Check 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, (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++) { 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; } } 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, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin opfamily %u contains invalid ORDER BY specification for operator %u", opfamilyoid, oprform->amopopr))); /* Count operators that are specifically for the named opclass */ if (oprform->amoplefttype == opcintype && oprform->amoprighttype == opcintype) numclassops++; } /* Check that the named opclass is complete */ if (numclassops == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin opclass %u is missing operator(s)", opclassoid))); for (i = 1; i <= BRIN_MANDATORY_NPROCS; i++) { if ((classfuncbits & (1 << i)) != 0) continue; /* got it */ ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin opclass %u is missing required support function %d", opclassoid, i))); } ReleaseCatCacheList(proclist); ReleaseCatCacheList(oprlist); return true; }
/* * sepgsql_relation_drop * * It checks privileges to drop the supplied relation. */ void sepgsql_relation_drop(Oid relOid) { ObjectAddress object; char *audit_name; uint16_t tclass = 0; char relkind; relkind = get_rel_relkind(relOid); if (relkind == RELKIND_RELATION) tclass = SEPG_CLASS_DB_TABLE; else if (relkind == RELKIND_SEQUENCE) tclass = SEPG_CLASS_DB_SEQUENCE; else if (relkind == RELKIND_VIEW) tclass = SEPG_CLASS_DB_VIEW; else return; /* * check db_schema:{remove_name} permission */ object.classId = NamespaceRelationId; object.objectId = get_rel_namespace(relOid); object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_SCHEMA, SEPG_DB_SCHEMA__REMOVE_NAME, audit_name, true); pfree(audit_name); /* * check db_table/sequence/view:{drop} permission */ object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = 0; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, tclass, SEPG_DB_TABLE__DROP, audit_name, true); pfree(audit_name); /* * check db_column:{drop} permission */ if (relkind == RELKIND_RELATION) { Form_pg_attribute attForm; CatCList *attrList; HeapTuple atttup; int i; attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid)); for (i = 0; i < attrList->n_members; i++) { atttup = &attrList->members[i]->tuple; attForm = (Form_pg_attribute) GETSTRUCT(atttup); if (attForm->attisdropped) continue; object.classId = RelationRelationId; object.objectId = relOid; object.objectSubId = attForm->attnum; audit_name = getObjectDescription(&object); sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_COLUMN, SEPG_DB_COLUMN__DROP, audit_name, true); pfree(audit_name); } ReleaseCatCacheList(attrList); } }
/* * 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; char *opclassname; HeapTuple familytup; Form_pg_opfamily familyform; char *opfamilyname; CatCList *proclist, *oprlist; uint64 allfuncs = 0; uint64 allops = 0; List *grouplist; OpFamilyOpFuncGroup *opclassgroup; int i; ListCell *lc; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; opclassname = NameStr(classform->opcname); /* 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)); /* 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 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 operator family \"%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 operator family \"%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 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); /* Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%s\" contains operator %s with invalid strategy number %d", opfamilyname, format_operator(oprform->amopopr), oprform->amopstrategy))); result = false; } else { /* * 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; } /* brin doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) { ereport(INFO, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("brin operator family \"%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 operator family \"%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; /* * 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 operator family \"%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 operator family \"%s\" is missing support function(s) for types %s and %s", opfamilyname, format_type_be(thisgroup->lefttype), format_type_be(thisgroup->righttype)))); result = false; } }
/* * Validator for a btree 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 btvalidate(Oid opclassoid) { HeapTuple classtup; Form_pg_opclass classform; Oid opfamilyoid; Oid opcintype; int numclassops; int32 classfuncbits; CatCList *proclist, *oprlist; Oid lastlefttype, lastrighttype; int numOps; int i, j; /* Fetch opclass information */ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid)); if (!HeapTupleIsValid(classtup)) elog(ERROR, "cache lookup failed for operator class %u", opclassoid); classform = (Form_pg_opclass) GETSTRUCT(classtup); opfamilyoid = classform->opcfamily; opcintype = classform->opcintype; ReleaseSysCache(classtup); /* Fetch all operators and support functions of the opfamily */ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid)); proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid)); /* We rely on the oprlist to be ordered */ if (!oprlist->ordered) elog(ERROR, "cannot validate btree opclass without ordered data"); /* We'll track the ops and functions belonging to the named opclass */ numclassops = 0; classfuncbits = 0; /* Check 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); /* Check that only allowed procedure numbers exist */ if (procform->amprocnum != BTORDER_PROC && procform->amprocnum != BTSORTSUPPORT_PROC) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree 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); } /* Check operators */ lastlefttype = lastrighttype = InvalidOid; numOps = 0; for (i = 0; i < oprlist->n_members; i++) { HeapTuple oprtup = &oprlist->members[i]->tuple; Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup); /* Check that only allowed strategy numbers exist */ if (oprform->amopstrategy < 1 || oprform->amopstrategy > BTMaxStrategyNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree opfamily %u contains invalid strategy number %d for operator %u", opfamilyoid, oprform->amopstrategy, oprform->amopopr))); /* * Check that we have all strategies for each supported datatype * combination. This is easy since the list will be sorted in * datatype order and there can't be duplicate strategy numbers. */ if (oprform->amoplefttype == lastlefttype && oprform->amoprighttype == lastrighttype) numOps++; else { /* reached a group boundary, so check ... */ if (numOps > 0 && numOps != BTMaxStrategyNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree opfamily %u has a partial set of operators for datatypes %s and %s", opfamilyoid, format_type_be(lastlefttype), format_type_be(lastrighttype)))); /* ... and reset for new group */ lastlefttype = oprform->amoplefttype; lastrighttype = oprform->amoprighttype; numOps = 1; } /* * There should be a relevant support function for each operator, but * we only need to check this once per pair of datatypes. */ if (numOps == 1) { bool found = false; for (j = 0; j < proclist->n_members; j++) { HeapTuple proctup = &proclist->members[j]->tuple; Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup); if (procform->amprocnum == BTORDER_PROC && procform->amproclefttype == oprform->amoplefttype && procform->amprocrighttype == oprform->amoprighttype) { found = true; break; } } if (!found) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree opfamily %u lacks support function for operator %u", opfamilyoid, oprform->amopopr))); } /* btree doesn't support ORDER BY operators */ if (oprform->amoppurpose != AMOP_SEARCH || OidIsValid(oprform->amopsortfamily)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree opfamily %u contains invalid ORDER BY specification for operator %u", opfamilyoid, oprform->amopopr))); /* Count operators that are specifically for the named opclass */ if (oprform->amoplefttype == opcintype && oprform->amoprighttype == opcintype) numclassops++; } /* don't forget to check the last batch of operators for completeness */ if (numOps > 0 && numOps != BTMaxStrategyNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree opfamily %u has a partial set of operators for datatypes %s and %s", opfamilyoid, format_type_be(lastlefttype), format_type_be(lastrighttype)))); /* Check that the named opclass is complete */ if (numclassops != BTMaxStrategyNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree opclass %u is missing operator(s)", opclassoid))); if ((classfuncbits & (1 << BTORDER_PROC)) == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("btree opclass %u is missing required support function", opclassoid))); ReleaseCatCacheList(proclist); ReleaseCatCacheList(oprlist); return true; }