/*------------------------------------------------------------------------- * * pg_namespace.c * routines to support manipulation of the pg_namespace relation * * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/catalog/pg_namespace.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/htup_details.h" #include "access/table.h" #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/objectaccess.h" #include "catalog/pg_namespace.h" #include "utils/builtins.h" #include "utils/rel.h" #include "utils/syscache.h" /* ---------------- * NamespaceCreate * * Create a namespace (schema) with the given name and owner OID. * * If isTemp is true, this schema is a per-backend schema for holding * temporary tables. Currently, it is used to prevent it from being * linked as a member of any active extension. (If someone does CREATE * TEMP TABLE in an extension script, we don't want the temp schema to * become part of the extension). And to avoid checking for default ACL * for temp namespace (as it is not necessary). * --------------- */ Oid NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp) { Relation nspdesc; HeapTuple tup; Oid nspoid; bool nulls[Natts_pg_namespace]; Datum values[Natts_pg_namespace]; NameData nname; TupleDesc tupDesc; ObjectAddress myself; int i; Acl *nspacl; /* sanity checks */ if (!nspName) elog(ERROR, "no namespace name supplied"); /* make sure there is no existing namespace of same name */ if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(nspName))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_SCHEMA), errmsg("schema \"%s\" already exists", nspName))); if (!isTemp) nspacl = get_user_default_acl(OBJECT_SCHEMA, ownerId, InvalidOid); else nspacl = NULL; nspdesc = table_open(NamespaceRelationId, RowExclusiveLock); tupDesc = nspdesc->rd_att; /* initialize nulls and values */ for (i = 0; i < Natts_pg_namespace; i++) { nulls[i] = false; values[i] = (Datum) NULL; } nspoid = GetNewOidWithIndex(nspdesc, NamespaceOidIndexId, Anum_pg_namespace_oid); values[Anum_pg_namespace_oid - 1] = ObjectIdGetDatum(nspoid); namestrcpy(&nname, nspName); values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); if (nspacl != NULL) values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(nspacl); else nulls[Anum_pg_namespace_nspacl - 1] = true; tup = heap_form_tuple(tupDesc, values, nulls); CatalogTupleInsert(nspdesc, tup); Assert(OidIsValid(nspoid)); table_close(nspdesc, RowExclusiveLock); /* Record dependencies */ myself.classId = NamespaceRelationId; myself.objectId = nspoid; myself.objectSubId = 0; /* dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); /* dependencies on roles mentioned in default ACL */ recordDependencyOnNewAcl(NamespaceRelationId, nspoid, 0, ownerId, nspacl); /* dependency on extension ... but not for magic temp schemas */ if (!isTemp) recordDependencyOnCurrentExtension(&myself, false); /* Post creation hook for new schema */ InvokeObjectPostCreateHook(NamespaceRelationId, nspoid, 0); return nspoid; }