* because the heaptuples data structure is all in local memory, not in
* the shared buffer.
*/
- if (IsSystemRelation(relation))
+ if (IsCatalogRelation(relation))
{
for (i = 0; i < ntuples; i++)
CacheInvalidateHeapTuple(relation, heaptuples[i], NULL);
* themselves. ACL_USAGE is if we ever have system sequences.
*/
if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
- IsSystemClass(classForm) &&
+ IsSystemClass(table_oid, classForm) &&
classForm->relkind != RELKIND_VIEW &&
!has_rolcatupdate(roleid) &&
!allowSystemTableMods)
/*
* IsSystemRelation
- * True iff the relation is a system catalog relation.
+ * True iff the relation is either a system catalog or toast table.
+ * By a system catalog, we mean one that created in the pg_catalog schema
+ * during initdb. User-created relations in pg_catalog don't count as
+ * system catalogs.
*
* NB: TOAST relations are considered system relations by this test
* for compatibility with the old IsSystemRelationName function.
* This is appropriate in many places but not all. Where it's not,
- * also check IsToastRelation.
- *
- * We now just test if the relation is in the system catalog namespace;
- * so it's no longer necessary to forbid user relations from having
- * names starting with pg_.
+ * also check IsToastRelation or use IsCatalogRelation().
*/
bool
IsSystemRelation(Relation relation)
{
- return IsSystemNamespace(RelationGetNamespace(relation)) ||
- IsToastNamespace(RelationGetNamespace(relation));
+ return IsSystemClass(RelationGetRelid(relation), relation->rd_rel);
}
/*
* search pg_class directly.
*/
bool
-IsSystemClass(Form_pg_class reltuple)
+IsSystemClass(Oid relid, Form_pg_class reltuple)
{
- Oid relnamespace = reltuple->relnamespace;
+ return IsToastClass(reltuple) || IsCatalogClass(relid, reltuple);
+}
+
+/*
+ * IsCatalogRelation
+ * True iff the relation is a system catalog, or the toast table for
+ * a system catalog. By a system catalog, we mean one that created
+ * in the pg_catalog schema during initdb. As with IsSystemRelation(),
+ * user-created relations in pg_catalog don't count as system catalogs.
+ *
+ * Note that IsSystemRelation() returns true for ALL toast relations,
+ * but this function returns true only for toast relations of system
+ * catalogs.
+ */
+bool
+IsCatalogRelation(Relation relation)
+{
+ return IsCatalogClass(RelationGetRelid(relation), relation->rd_rel);
+}
- return IsSystemNamespace(relnamespace) ||
- IsToastNamespace(relnamespace);
+/*
+ * IsCatalogClass
+ * True iff the relation is a system catalog relation.
+ *
+ * Check IsCatalogRelation() for details.
+ */
+bool
+IsCatalogClass(Oid relid, Form_pg_class reltuple)
+{
+ Oid relnamespace = reltuple->relnamespace;
+
+ /*
+ * Never consider relations outside pg_catalog/pg_toast to be catalog
+ * relations.
+ */
+ if (!IsSystemNamespace(relnamespace) && !IsToastNamespace(relnamespace))
+ return false;
+
+ /* ----
+ * Check whether the oid was assigned during initdb, when creating the
+ * initial template database. Minus the relations in information_schema
+ * excluded above, these are integral part of the system.
+ * We could instead check whether the relation is pinned in pg_depend, but
+ * this is noticeably cheaper and doesn't require catalog access.
+ *
+ * This test is safe since even a oid wraparound will preserve this
+ * property (c.f. GetNewObjectId()) and it has the advantage that it works
+ * correctly even if a user decides to create a relation in the pg_catalog
+ * namespace.
+ * ----
+ */
+ return relid < FirstNormalObjectId;
}
/*
Assert(OidIsValid(relid));
/*
- * sanity checks
+ * Don't allow creating relations in pg_catalog directly, even though it
+ * is allowed to move user defined relations there. Semantics with search
+ * paths including pg_catalog are too confusing for now.
+ *
+ * But allow creating indexes on relations in pg_catalog even if
+ * allow_system_table_mods = off, upper layers already guarantee it's on a
+ * user defined relation, not a system one.
*/
if (!allow_system_table_mods &&
- (IsSystemNamespace(relnamespace) || IsToastNamespace(relnamespace)) &&
+ ((IsSystemNamespace(relnamespace) && relkind != RELKIND_INDEX) ||
+ IsToastNamespace(relnamespace)) &&
IsNormalProcessingMode())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
* ones the dependency changes would change. It's too late to be
* making any data changes to the target catalog.
*/
- if (IsSystemClass(relform1))
+ if (IsSystemClass(r1, relform1))
elog(ERROR, "cannot swap toast files by links for system catalogs");
/* Delete old dependencies */
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
+ Oid relid = HeapTupleGetOid(tuple);
if (classtuple->relkind != RELKIND_RELATION &&
classtuple->relkind != RELKIND_MATVIEW)
continue;
/* Check user/system classification, and optionally skip */
- if (IsSystemClass(classtuple))
+ if (IsSystemClass(relid, classtuple))
{
if (!do_system)
continue;
continue; /* got it already */
old = MemoryContextSwitchTo(private_context);
- relids = lappend_oid(relids, HeapTupleGetOid(tuple));
+ relids = lappend_oid(relids, relid);
MemoryContextSwitchTo(old);
}
heap_endscan(scan);
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
rel->relname);
- if (!allowSystemTableMods && IsSystemClass(classform))
+ if (!allowSystemTableMods && IsSystemClass(relOid, classform))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
if (!pg_class_ownercheck(myrelid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
NameStr(classform->relname));
- if (!allowSystemTableMods && IsSystemClass(classform))
+ if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
/* No system table modifications unless explicitly allowed. */
- if (!allowSystemTableMods && IsSystemClass(classform))
+ if (!allowSystemTableMods && IsSystemClass(relid, classform))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
/* you must own the table to rename one of its triggers */
if (!pg_class_ownercheck(relid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname);
- if (!allowSystemTableMods && IsSystemClass(form))
+ if (!allowSystemTableMods && IsSystemClass(relid, form))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
* Don't bother with indexes for an inheritance parent, either.
*/
if (inhparent ||
- (IgnoreSystemIndexes && IsSystemClass(relation->rd_rel)))
+ (IgnoreSystemIndexes && IsSystemRelation(relation)))
hasindex = false;
else
hasindex = relation->rd_rel->relhasindex;
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table or view", rv->relname)));
- if (!allowSystemTableMods && IsSystemClass(form))
+ if (!allowSystemTableMods && IsSystemClass(relid, form))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
if (noCatalogs)
{
if (!allowSystemTableMods &&
- IsSystemClass((Form_pg_class) GETSTRUCT(tuple)))
+ IsSystemClass(relOid, (Form_pg_class) GETSTRUCT(tuple)))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
/*
* We only need to worry about invalidation for tuples that are in system
- * relations; user-relation tuples are never in catcaches and can't affect
+ * catalogs; user-relation tuples are never in catcaches and can't affect
* the relcache either.
*/
- if (!IsSystemRelation(relation))
+ if (!IsCatalogRelation(relation))
return;
/*
- * TOAST tuples can likewise be ignored here. Note that TOAST tables are
- * considered system relations so they are not filtered by the above test.
+ * IsCatalogRelation() will return true for TOAST tables of system
+ * catalogs, but we don't care about those, either.
*/
if (IsToastRelation(relation))
return;
extern bool IsSystemRelation(Relation relation);
extern bool IsToastRelation(Relation relation);
+extern bool IsCatalogRelation(Relation relation);
-extern bool IsSystemClass(Form_pg_class reltuple);
+extern bool IsSystemClass(Oid relid, Form_pg_class reltuple);
extern bool IsToastClass(Form_pg_class reltuple);
+extern bool IsCatalogClass(Oid relid, Form_pg_class reltuple);
extern bool IsSystemNamespace(Oid namespaceId);
extern bool IsToastNamespace(Oid namespaceId);
0 | t
(1 row)
+-- Checks on creating and manipulation of user defined relations in
+-- pg_catalog.
+--
+-- XXX: It would be useful to add checks around trying to manipulate
+-- catalog tables, but that might have ugly consequences when run
+-- against an existing server with allow_system_table_mods = on.
+SHOW allow_system_table_mods;
+ allow_system_table_mods
+-------------------------
+ off
+(1 row)
+
+-- disallowed because of search_path issues with pg_dump
+CREATE TABLE pg_catalog.new_system_table();
+ERROR: permission denied to create "pg_catalog.new_system_table"
+DETAIL: System catalog modifications are currently disallowed.
+-- instead create in public first, move to catalog
+CREATE TABLE new_system_table(id serial primary key, othercol text);
+ALTER TABLE new_system_table SET SCHEMA pg_catalog;
+-- XXX: it's currently impossible to move relations out of pg_catalog
+ALTER TABLE new_system_table SET SCHEMA public;
+ERROR: cannot remove dependency on schema pg_catalog because it is a system object
+-- move back, will currently error out, already there
+ALTER TABLE new_system_table SET SCHEMA pg_catalog;
+ERROR: table new_system_table is already in schema "pg_catalog"
+ALTER TABLE new_system_table RENAME TO old_system_table;
+CREATE INDEX old_system_table__othercol ON old_system_table (othercol);
+INSERT INTO old_system_table(othercol) VALUES ('somedata'), ('otherdata');
+UPDATE old_system_table SET id = -id;
+DELETE FROM old_system_table WHERE othercol = 'somedata';
+TRUNCATE old_system_table;
+ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
+ALTER TABLE old_system_table DROP COLUMN othercol;
+DROP TABLE old_system_table;
FROM pg_class
WHERE relkind IN ('r', 'i', 'S', 't', 'm')
) mapped;
+
+-- Checks on creating and manipulation of user defined relations in
+-- pg_catalog.
+--
+-- XXX: It would be useful to add checks around trying to manipulate
+-- catalog tables, but that might have ugly consequences when run
+-- against an existing server with allow_system_table_mods = on.
+
+SHOW allow_system_table_mods;
+-- disallowed because of search_path issues with pg_dump
+CREATE TABLE pg_catalog.new_system_table();
+-- instead create in public first, move to catalog
+CREATE TABLE new_system_table(id serial primary key, othercol text);
+ALTER TABLE new_system_table SET SCHEMA pg_catalog;
+
+-- XXX: it's currently impossible to move relations out of pg_catalog
+ALTER TABLE new_system_table SET SCHEMA public;
+-- move back, will currently error out, already there
+ALTER TABLE new_system_table SET SCHEMA pg_catalog;
+ALTER TABLE new_system_table RENAME TO old_system_table;
+CREATE INDEX old_system_table__othercol ON old_system_table (othercol);
+INSERT INTO old_system_table(othercol) VALUES ('somedata'), ('otherdata');
+UPDATE old_system_table SET id = -id;
+DELETE FROM old_system_table WHERE othercol = 'somedata';
+TRUNCATE old_system_table;
+ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
+ALTER TABLE old_system_table DROP COLUMN othercol;
+DROP TABLE old_system_table;