Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * schemacmds.c
4 : * schema creation/manipulation commands
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/commands/schemacmds.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/htup_details.h"
18 : #include "access/table.h"
19 : #include "access/xact.h"
20 : #include "catalog/catalog.h"
21 : #include "catalog/dependency.h"
22 : #include "catalog/indexing.h"
23 : #include "catalog/namespace.h"
24 : #include "catalog/objectaccess.h"
25 : #include "catalog/pg_authid.h"
26 : #include "catalog/pg_database.h"
27 : #include "catalog/pg_namespace.h"
28 : #include "commands/dbcommands.h"
29 : #include "commands/event_trigger.h"
30 : #include "commands/schemacmds.h"
31 : #include "miscadmin.h"
32 : #include "parser/parse_utilcmd.h"
33 : #include "tcop/utility.h"
34 : #include "utils/acl.h"
35 : #include "utils/builtins.h"
36 : #include "utils/rel.h"
37 : #include "utils/syscache.h"
38 :
39 : static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
40 :
41 : /*
42 : * CREATE SCHEMA
43 : *
44 : * Note: caller should pass in location information for the whole
45 : * CREATE SCHEMA statement, which in turn we pass down as the location
46 : * of the component commands. This comports with our general plan of
47 : * reporting location/len for the whole command even when executing
48 : * a subquery.
49 : */
50 : Oid
2276 tgl 51 GIC 674 : CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
2276 tgl 52 ECB : int stmt_location, int stmt_len)
53 : {
2878 bruce 54 GIC 674 : const char *schemaName = stmt->schemaname;
7632 tgl 55 ECB : Oid namespaceId;
56 : OverrideSearchPath *overridePath;
57 : List *parsetree_list;
58 : ListCell *parsetree_item;
59 : Oid owner_uid;
60 : Oid saved_uid;
61 : int save_sec_context;
62 : AclResult aclresult;
63 : ObjectAddress address;
64 :
4869 tgl 65 GIC 674 : GetUserIdAndSecContext(&saved_uid, &save_sec_context);
7664 tgl 66 ECB :
67 : /*
68 : * Who is supposed to own the new schema?
69 : */
2953 alvherre 70 GIC 674 : if (stmt->authrole)
2953 alvherre 71 CBC 34 : owner_uid = get_rolespec_oid(stmt->authrole, false);
7522 bruce 72 ECB : else
6494 tgl 73 GIC 640 : owner_uid = saved_uid;
7664 tgl 74 ECB :
75 : /* fill schema name with the user name if not specified */
2953 alvherre 76 GIC 668 : if (!schemaName)
2953 alvherre 77 ECB : {
78 : HeapTuple tuple;
79 :
2953 alvherre 80 UIC 0 : tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(owner_uid));
2953 alvherre 81 UBC 0 : if (!HeapTupleIsValid(tuple))
82 0 : elog(ERROR, "cache lookup failed for role %u", owner_uid);
2953 alvherre 83 EUB : schemaName =
2953 alvherre 84 UIC 0 : pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
2953 alvherre 85 UBC 0 : ReleaseSysCache(tuple);
2953 alvherre 86 EUB : }
87 :
88 : /*
89 : * To create a schema, must have schema-create privilege on the current
90 : * database and must be able to become the target role (this does not
91 : * imply that the target role itself must have create-schema privilege).
92 : * The latter provision guards against "giveaway" attacks. Note that a
93 : * superuser will always have both of these privileges a fortiori.
94 : */
147 peter 95 GNC 668 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, saved_uid, ACL_CREATE);
7652 tgl 96 CBC 668 : if (aclresult != ACLCHECK_OK)
1954 peter_e 97 LBC 0 : aclcheck_error(aclresult, OBJECT_DATABASE,
7191 tgl 98 UBC 0 : get_database_name(MyDatabaseId));
7652 tgl 99 EUB :
142 rhaas 100 GNC 668 : check_can_set_role(saved_uid, owner_uid);
6478 tgl 101 ECB :
102 : /* Additional check to protect reserved schema names */
7664 tgl 103 GIC 665 : if (!allowSystemTableMods && IsReservedName(schemaName))
7205 tgl 104 CBC 1 : ereport(ERROR,
7205 tgl 105 ECB : (errcode(ERRCODE_RESERVED_NAME),
106 : errmsg("unacceptable schema name \"%s\"", schemaName),
107 : errdetail("The prefix \"pg_\" is reserved for system schemas.")));
108 :
109 : /*
110 : * If if_not_exists was given and the schema already exists, bail out.
111 : * (Note: we needn't check this when not if_not_exists, because
112 : * NamespaceCreate will complain anyway.) We could do this before making
113 : * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
114 : * creation-permission check first, we do likewise.
115 : */
244 tgl 116 GIC 664 : if (stmt->if_not_exists)
3840 tgl 117 ECB : {
244 tgl 118 GIC 17 : namespaceId = get_namespace_oid(schemaName, true);
244 tgl 119 CBC 17 : if (OidIsValid(namespaceId))
244 tgl 120 ECB : {
121 : /*
122 : * If we are in an extension script, insist that the pre-existing
123 : * object be a member of the extension, to avoid security risks.
124 : */
244 tgl 125 GIC 12 : ObjectAddressSet(address, NamespaceRelationId, namespaceId);
244 tgl 126 CBC 12 : checkMembershipInCurrentExtension(&address);
244 tgl 127 ECB :
128 : /* OK to skip */
244 tgl 129 GIC 11 : ereport(NOTICE,
244 tgl 130 ECB : (errcode(ERRCODE_DUPLICATE_SCHEMA),
131 : errmsg("schema \"%s\" already exists, skipping",
132 : schemaName)));
244 tgl 133 GIC 11 : return InvalidOid;
244 tgl 134 ECB : }
135 : }
136 :
137 : /*
138 : * If the requested authorization is different from the current user,
139 : * temporarily set the current user so that the object(s) will be created
140 : * with the correct ownership.
141 : *
142 : * (The setting will be restored at the end of this routine, or in case of
143 : * error, transaction abort will clean things up.)
144 : */
6478 tgl 145 GIC 652 : if (saved_uid != owner_uid)
4869 tgl 146 CBC 17 : SetUserIdAndSecContext(owner_uid,
2118 tgl 147 ECB : save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
148 :
149 : /* Create the schema's namespace */
4049 tgl 150 GIC 652 : namespaceId = NamespaceCreate(schemaName, owner_uid, false);
7664 tgl 151 ECB :
152 : /* Advance cmd counter to make the namespace visible */
7664 tgl 153 GIC 649 : CommandCounterIncrement();
7664 tgl 154 ECB :
155 : /*
156 : * Temporarily make the new namespace be the front of the search path, as
157 : * well as the default creation target namespace. This will be undone at
158 : * the end of this routine, or upon error.
159 : */
5861 tgl 160 GIC 649 : overridePath = GetOverrideSearchPath(CurrentMemoryContext);
5861 tgl 161 CBC 649 : overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
5861 tgl 162 ECB : /* XXX should we clear overridePath->useTemp? */
5861 tgl 163 GIC 649 : PushOverrideSearchPath(overridePath);
7632 tgl 164 ECB :
165 : /*
166 : * Report the new schema to possibly interested event triggers. Note we
167 : * must do this here and not in ProcessUtilitySlow because otherwise the
168 : * objects created below are reported before the schema, which would be
169 : * wrong.
170 : */
2890 alvherre 171 GIC 649 : ObjectAddressSet(address, NamespaceRelationId, namespaceId);
2890 alvherre 172 CBC 649 : EventTriggerCollectSimpleCommand(address, InvalidObjectAddress,
2890 alvherre 173 ECB : (Node *) stmt);
174 :
175 : /*
176 : * Examine the list of commands embedded in the CREATE SCHEMA command, and
177 : * reorganize them into a sequentially executable order with no forward
178 : * references. Note that the result is still a list of raw parsetrees ---
179 : * we cannot, in general, run parse analysis on one statement until we
180 : * have actually executed the prior ones.
181 : */
5769 tgl 182 GIC 649 : parsetree_list = transformCreateSchemaStmt(stmt);
7664 tgl 183 ECB :
184 : /*
185 : * Execute each command contained in the CREATE SCHEMA. Since the grammar
186 : * allows only utility commands in CREATE SCHEMA, there is no need to pass
187 : * them through parse_analyze_*() or the rewriter; we can just hand them
188 : * straight to ProcessUtility.
189 : */
7664 tgl 190 GIC 821 : foreach(parsetree_item, parsetree_list)
7664 tgl 191 ECB : {
5769 tgl 192 GIC 175 : Node *stmt = (Node *) lfirst(parsetree_item);
2276 tgl 193 ECB : PlannedStmt *wrapper;
194 :
195 : /* need to make a wrapper PlannedStmt */
2276 tgl 196 GIC 175 : wrapper = makeNode(PlannedStmt);
2276 tgl 197 CBC 175 : wrapper->commandType = CMD_UTILITY;
198 175 : wrapper->canSetTag = false;
199 175 : wrapper->utilityStmt = stmt;
200 175 : wrapper->stmt_location = stmt_location;
201 175 : wrapper->stmt_len = stmt_len;
5769 tgl 202 ECB :
203 : /* do this step */
2276 tgl 204 GIC 175 : ProcessUtility(wrapper,
5769 tgl 205 ECB : queryString,
206 : false,
207 : PROCESS_UTILITY_SUBCOMMAND,
208 : NULL,
209 : NULL,
210 : None_Receiver,
211 : NULL);
212 :
213 : /* make sure later steps can see the object created here */
5769 tgl 214 GIC 172 : CommandCounterIncrement();
7664 tgl 215 ECB : }
216 :
217 : /* Reset search path to normal state */
5861 tgl 218 GIC 646 : PopOverrideSearchPath();
7632 tgl 219 ECB :
220 : /* Reset current user and security context */
4869 tgl 221 GIC 646 : SetUserIdAndSecContext(saved_uid, save_sec_context);
3759 rhaas 222 ECB :
3759 rhaas 223 GIC 646 : return namespaceId;
7664 tgl 224 ECB : }
225 :
226 :
227 : /*
228 : * Rename schema
229 : */
230 : ObjectAddress
7226 peter_e 231 GIC 10 : RenameSchema(const char *oldname, const char *newname)
7226 peter_e 232 ECB : {
233 : Oid nspOid;
234 : HeapTuple tup;
235 : Relation rel;
236 : AclResult aclresult;
237 : ObjectAddress address;
238 : Form_pg_namespace nspform;
239 :
1539 andres 240 GIC 10 : rel = table_open(NamespaceRelationId, RowExclusiveLock);
7226 peter_e 241 ECB :
4802 rhaas 242 GIC 10 : tup = SearchSysCacheCopy1(NAMESPACENAME, CStringGetDatum(oldname));
7226 peter_e 243 CBC 10 : if (!HeapTupleIsValid(tup))
7226 peter_e 244 LBC 0 : ereport(ERROR,
7205 tgl 245 EUB : (errcode(ERRCODE_UNDEFINED_SCHEMA),
246 : errmsg("schema \"%s\" does not exist", oldname)));
247 :
1601 andres 248 GIC 10 : nspform = (Form_pg_namespace) GETSTRUCT(tup);
1601 andres 249 CBC 10 : nspOid = nspform->oid;
3759 rhaas 250 ECB :
251 : /* make sure the new name doesn't exist */
4630 rhaas 252 GIC 10 : if (OidIsValid(get_namespace_oid(newname, true)))
7226 peter_e 253 LBC 0 : ereport(ERROR,
7205 tgl 254 EUB : (errcode(ERRCODE_DUPLICATE_SCHEMA),
255 : errmsg("schema \"%s\" already exists", newname)));
256 :
257 : /* must be owner */
147 peter 258 GNC 10 : if (!object_ownercheck(NamespaceRelationId, nspOid, GetUserId()))
1954 peter_e 259 LBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
7191 tgl 260 EUB : oldname);
261 :
262 : /* must have CREATE privilege on database */
147 peter 263 GNC 10 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
7226 peter_e 264 CBC 10 : if (aclresult != ACLCHECK_OK)
1954 peter_e 265 LBC 0 : aclcheck_error(aclresult, OBJECT_DATABASE,
7191 tgl 266 UBC 0 : get_database_name(MyDatabaseId));
7226 peter_e 267 EUB :
7226 peter_e 268 GIC 10 : if (!allowSystemTableMods && IsReservedName(newname))
7205 tgl 269 LBC 0 : ereport(ERROR,
7205 tgl 270 EUB : (errcode(ERRCODE_RESERVED_NAME),
271 : errmsg("unacceptable schema name \"%s\"", newname),
272 : errdetail("The prefix \"pg_\" is reserved for system schemas.")));
273 :
274 : /* rename */
1601 andres 275 GIC 10 : namestrcpy(&nspform->nspname, newname);
2259 alvherre 276 CBC 10 : CatalogTupleUpdate(rel, &tup->t_self, tup);
7226 peter_e 277 ECB :
1601 andres 278 GIC 10 : InvokeObjectPostAlterHook(NamespaceRelationId, nspOid, 0);
3675 rhaas 279 ECB :
2959 alvherre 280 GIC 10 : ObjectAddressSet(address, NamespaceRelationId, nspOid);
2959 alvherre 281 ECB :
1539 andres 282 GIC 10 : table_close(rel, NoLock);
7226 peter_e 283 CBC 10 : heap_freetuple(tup);
3759 rhaas 284 ECB :
2959 alvherre 285 GIC 10 : return address;
7226 peter_e 286 ECB : }
287 :
288 : void
201 pg 289 GNC 3 : AlterSchemaOwner_oid(Oid schemaoid, Oid newOwnerId)
6348 alvherre 290 ECB : {
291 : HeapTuple tup;
292 : Relation rel;
293 :
1539 andres 294 GIC 3 : rel = table_open(NamespaceRelationId, RowExclusiveLock);
6348 alvherre 295 ECB :
201 pg 296 GNC 3 : tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(schemaoid));
6348 alvherre 297 CBC 3 : if (!HeapTupleIsValid(tup))
201 pg 298 UNC 0 : elog(ERROR, "cache lookup failed for schema %u", schemaoid);
6348 alvherre 299 EUB :
6348 alvherre 300 GIC 3 : AlterSchemaOwner_internal(tup, rel, newOwnerId);
6348 alvherre 301 ECB :
6348 alvherre 302 GIC 3 : ReleaseSysCache(tup);
6348 alvherre 303 ECB :
1539 andres 304 GIC 3 : table_close(rel, RowExclusiveLock);
6348 alvherre 305 CBC 3 : }
6348 alvherre 306 ECB :
307 :
308 : /*
309 : * Change schema owner
310 : */
311 : ObjectAddress
6494 tgl 312 GIC 26 : AlterSchemaOwner(const char *name, Oid newOwnerId)
6862 tgl 313 ECB : {
314 : Oid nspOid;
315 : HeapTuple tup;
316 : Relation rel;
317 : ObjectAddress address;
318 : Form_pg_namespace nspform;
319 :
1539 andres 320 GIC 26 : rel = table_open(NamespaceRelationId, RowExclusiveLock);
6862 tgl 321 ECB :
4802 rhaas 322 GIC 26 : tup = SearchSysCache1(NAMESPACENAME, CStringGetDatum(name));
6862 tgl 323 CBC 26 : if (!HeapTupleIsValid(tup))
6862 tgl 324 LBC 0 : ereport(ERROR,
6862 tgl 325 EUB : (errcode(ERRCODE_UNDEFINED_SCHEMA),
326 : errmsg("schema \"%s\" does not exist", name)));
327 :
1601 andres 328 GIC 26 : nspform = (Form_pg_namespace) GETSTRUCT(tup);
1601 andres 329 CBC 26 : nspOid = nspform->oid;
3759 rhaas 330 ECB :
6348 alvherre 331 GIC 26 : AlterSchemaOwner_internal(tup, rel, newOwnerId);
6348 alvherre 332 ECB :
2959 alvherre 333 GIC 26 : ObjectAddressSet(address, NamespaceRelationId, nspOid);
2959 alvherre 334 ECB :
6348 alvherre 335 GIC 26 : ReleaseSysCache(tup);
6348 alvherre 336 ECB :
1539 andres 337 GIC 26 : table_close(rel, RowExclusiveLock);
3759 rhaas 338 ECB :
2959 alvherre 339 GIC 26 : return address;
6348 alvherre 340 ECB : }
341 :
342 : static void
6348 alvherre 343 GIC 29 : AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
6348 alvherre 344 ECB : {
345 : Form_pg_namespace nspForm;
346 :
6348 alvherre 347 GIC 29 : Assert(tup->t_tableOid == NamespaceRelationId);
6348 alvherre 348 CBC 29 : Assert(RelationGetRelid(rel) == NamespaceRelationId);
6348 alvherre 349 ECB :
6862 tgl 350 GIC 29 : nspForm = (Form_pg_namespace) GETSTRUCT(tup);
6862 tgl 351 ECB :
352 : /*
353 : * If the new owner is the same as the existing owner, consider the
354 : * command to have succeeded. This is for dump restoration purposes.
355 : */
6494 tgl 356 GIC 29 : if (nspForm->nspowner != newOwnerId)
6862 tgl 357 ECB : {
358 : Datum repl_val[Natts_pg_namespace];
359 : bool repl_null[Natts_pg_namespace];
360 : bool repl_repl[Natts_pg_namespace];
361 : Acl *newAcl;
362 : Datum aclDatum;
363 : bool isNull;
364 : HeapTuple newtuple;
365 : AclResult aclresult;
366 :
367 : /* Otherwise, must be owner of the existing object */
147 peter 368 GNC 20 : if (!object_ownercheck(NamespaceRelationId, nspForm->oid, GetUserId()))
1954 peter_e 369 LBC 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
6348 alvherre 370 UBC 0 : NameStr(nspForm->nspname));
6478 tgl 371 EUB :
372 : /* Must be able to become new owner */
142 rhaas 373 GNC 20 : check_can_set_role(GetUserId(), newOwnerId);
6825 tgl 374 ECB :
375 : /*
376 : * must have create-schema rights
377 : *
378 : * NOTE: This is different from other alter-owner checks in that the
379 : * current user is checked for create privileges instead of the
380 : * destination owner. This is consistent with the CREATE case for
381 : * schemas. Because superusers will always have this right, we need
382 : * no special case for them.
383 : */
147 peter 384 GNC 20 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(),
6478 tgl 385 ECB : ACL_CREATE);
6478 tgl 386 GIC 20 : if (aclresult != ACLCHECK_OK)
1954 peter_e 387 LBC 0 : aclcheck_error(aclresult, OBJECT_DATABASE,
6478 tgl 388 UBC 0 : get_database_name(MyDatabaseId));
6862 tgl 389 EUB :
5271 tgl 390 GIC 20 : memset(repl_null, false, sizeof(repl_null));
5271 tgl 391 CBC 20 : memset(repl_repl, false, sizeof(repl_repl));
6862 tgl 392 ECB :
5271 tgl 393 GIC 20 : repl_repl[Anum_pg_namespace_nspowner - 1] = true;
6494 tgl 394 CBC 20 : repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
6862 tgl 395 ECB :
396 : /*
397 : * Determine the modified ACL for the new owner. This is only
398 : * necessary when the ACL is non-null.
399 : */
6825 tgl 400 GIC 20 : aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
6825 tgl 401 ECB : Anum_pg_namespace_nspacl,
402 : &isNull);
6825 tgl 403 GIC 20 : if (!isNull)
6825 tgl 404 ECB : {
6825 tgl 405 GIC 2 : newAcl = aclnewowner(DatumGetAclP(aclDatum),
6494 tgl 406 ECB : nspForm->nspowner, newOwnerId);
5271 tgl 407 GIC 2 : repl_repl[Anum_pg_namespace_nspacl - 1] = true;
6825 tgl 408 CBC 2 : repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
6825 tgl 409 ECB : }
410 :
5271 tgl 411 GIC 20 : newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
6825 tgl 412 ECB :
2259 alvherre 413 GIC 20 : CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
6862 tgl 414 ECB :
6825 tgl 415 GIC 20 : heap_freetuple(newtuple);
6485 tgl 416 ECB :
417 : /* Update owner dependency reference */
1601 andres 418 GIC 20 : changeDependencyOnOwner(NamespaceRelationId, nspForm->oid,
6485 tgl 419 ECB : newOwnerId);
420 : }
421 :
3675 rhaas 422 GIC 29 : InvokeObjectPostAlterHook(NamespaceRelationId,
1601 andres 423 ECB : nspForm->oid, 0);
6862 tgl 424 GIC 29 : }
|