| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * pg_namespace.c |
| 4 | * routines to support manipulation of the pg_namespace relation |
| 5 | * |
| 6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 7 | * Portions Copyright (c) 1994, Regents of the University of California |
| 8 | * |
| 9 | * |
| 10 | * IDENTIFICATION |
| 11 | * src/backend/catalog/pg_namespace.c |
| 12 | * |
| 13 | *------------------------------------------------------------------------- |
| 14 | */ |
| 15 | #include "postgres.h" |
| 16 | |
| 17 | #include "access/htup_details.h" |
| 18 | #include "access/table.h" |
| 19 | #include "catalog/catalog.h" |
| 20 | #include "catalog/dependency.h" |
| 21 | #include "catalog/indexing.h" |
| 22 | #include "catalog/objectaccess.h" |
| 23 | #include "catalog/pg_namespace.h" |
| 24 | #include "utils/builtins.h" |
| 25 | #include "utils/rel.h" |
| 26 | #include "utils/syscache.h" |
| 27 | |
| 28 | |
| 29 | /* ---------------- |
| 30 | * NamespaceCreate |
| 31 | * |
| 32 | * Create a namespace (schema) with the given name and owner OID. |
| 33 | * |
| 34 | * If isTemp is true, this schema is a per-backend schema for holding |
| 35 | * temporary tables. Currently, it is used to prevent it from being |
| 36 | * linked as a member of any active extension. (If someone does CREATE |
| 37 | * TEMP TABLE in an extension script, we don't want the temp schema to |
| 38 | * become part of the extension). And to avoid checking for default ACL |
| 39 | * for temp namespace (as it is not necessary). |
| 40 | * --------------- |
| 41 | */ |
| 42 | Oid |
| 43 | NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp) |
| 44 | { |
| 45 | Relation nspdesc; |
| 46 | HeapTuple tup; |
| 47 | Oid nspoid; |
| 48 | bool nulls[Natts_pg_namespace]; |
| 49 | Datum values[Natts_pg_namespace]; |
| 50 | NameData nname; |
| 51 | TupleDesc tupDesc; |
| 52 | ObjectAddress myself; |
| 53 | int i; |
| 54 | Acl *nspacl; |
| 55 | |
| 56 | /* sanity checks */ |
| 57 | if (!nspName) |
| 58 | elog(ERROR, "no namespace name supplied" ); |
| 59 | |
| 60 | /* make sure there is no existing namespace of same name */ |
| 61 | if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(nspName))) |
| 62 | ereport(ERROR, |
| 63 | (errcode(ERRCODE_DUPLICATE_SCHEMA), |
| 64 | errmsg("schema \"%s\" already exists" , nspName))); |
| 65 | |
| 66 | if (!isTemp) |
| 67 | nspacl = get_user_default_acl(OBJECT_SCHEMA, ownerId, |
| 68 | InvalidOid); |
| 69 | else |
| 70 | nspacl = NULL; |
| 71 | |
| 72 | nspdesc = table_open(NamespaceRelationId, RowExclusiveLock); |
| 73 | tupDesc = nspdesc->rd_att; |
| 74 | |
| 75 | /* initialize nulls and values */ |
| 76 | for (i = 0; i < Natts_pg_namespace; i++) |
| 77 | { |
| 78 | nulls[i] = false; |
| 79 | values[i] = (Datum) NULL; |
| 80 | } |
| 81 | |
| 82 | nspoid = GetNewOidWithIndex(nspdesc, NamespaceOidIndexId, |
| 83 | Anum_pg_namespace_oid); |
| 84 | values[Anum_pg_namespace_oid - 1] = ObjectIdGetDatum(nspoid); |
| 85 | namestrcpy(&nname, nspName); |
| 86 | values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&nname); |
| 87 | values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(ownerId); |
| 88 | if (nspacl != NULL) |
| 89 | values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(nspacl); |
| 90 | else |
| 91 | nulls[Anum_pg_namespace_nspacl - 1] = true; |
| 92 | |
| 93 | |
| 94 | tup = heap_form_tuple(tupDesc, values, nulls); |
| 95 | |
| 96 | CatalogTupleInsert(nspdesc, tup); |
| 97 | Assert(OidIsValid(nspoid)); |
| 98 | |
| 99 | table_close(nspdesc, RowExclusiveLock); |
| 100 | |
| 101 | /* Record dependencies */ |
| 102 | myself.classId = NamespaceRelationId; |
| 103 | myself.objectId = nspoid; |
| 104 | myself.objectSubId = 0; |
| 105 | |
| 106 | /* dependency on owner */ |
| 107 | recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); |
| 108 | |
| 109 | /* dependences on roles mentioned in default ACL */ |
| 110 | recordDependencyOnNewAcl(NamespaceRelationId, nspoid, 0, ownerId, nspacl); |
| 111 | |
| 112 | /* dependency on extension ... but not for magic temp schemas */ |
| 113 | if (!isTemp) |
| 114 | recordDependencyOnCurrentExtension(&myself, false); |
| 115 | |
| 116 | /* Post creation hook for new schema */ |
| 117 | InvokeObjectPostCreateHook(NamespaceRelationId, nspoid, 0); |
| 118 | |
| 119 | return nspoid; |
| 120 | } |
| 121 | |