PostgreSQL Source Code  git master
schemacmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * schemacmds.c
4  * schema creation/manipulation commands
5  *
6  * Portions Copyright (c) 1996-2024, 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 "parser/scansup.h"
34 #include "tcop/utility.h"
35 #include "utils/acl.h"
36 #include "utils/builtins.h"
37 #include "utils/rel.h"
38 #include "utils/syscache.h"
39 
40 static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
41 
42 /*
43  * CREATE SCHEMA
44  *
45  * Note: caller should pass in location information for the whole
46  * CREATE SCHEMA statement, which in turn we pass down as the location
47  * of the component commands. This comports with our general plan of
48  * reporting location/len for the whole command even when executing
49  * a subquery.
50  */
51 Oid
52 CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
53  int stmt_location, int stmt_len)
54 {
55  const char *schemaName = stmt->schemaname;
56  Oid namespaceId;
57  List *parsetree_list;
58  ListCell *parsetree_item;
59  Oid owner_uid;
60  Oid saved_uid;
61  int save_sec_context;
62  int save_nestlevel;
63  char *nsp = namespace_search_path;
64  AclResult aclresult;
65  ObjectAddress address;
66  StringInfoData pathbuf;
67 
68  GetUserIdAndSecContext(&saved_uid, &save_sec_context);
69 
70  /*
71  * Who is supposed to own the new schema?
72  */
73  if (stmt->authrole)
74  owner_uid = get_rolespec_oid(stmt->authrole, false);
75  else
76  owner_uid = saved_uid;
77 
78  /* fill schema name with the user name if not specified */
79  if (!schemaName)
80  {
81  HeapTuple tuple;
82 
83  tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(owner_uid));
84  if (!HeapTupleIsValid(tuple))
85  elog(ERROR, "cache lookup failed for role %u", owner_uid);
86  schemaName =
88  ReleaseSysCache(tuple);
89  }
90 
91  /*
92  * To create a schema, must have schema-create privilege on the current
93  * database and must be able to become the target role (this does not
94  * imply that the target role itself must have create-schema privilege).
95  * The latter provision guards against "giveaway" attacks. Note that a
96  * superuser will always have both of these privileges a fortiori.
97  */
98  aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, saved_uid, ACL_CREATE);
99  if (aclresult != ACLCHECK_OK)
100  aclcheck_error(aclresult, OBJECT_DATABASE,
102 
103  check_can_set_role(saved_uid, owner_uid);
104 
105  /* Additional check to protect reserved schema names */
106  if (!allowSystemTableMods && IsReservedName(schemaName))
107  ereport(ERROR,
108  (errcode(ERRCODE_RESERVED_NAME),
109  errmsg("unacceptable schema name \"%s\"", schemaName),
110  errdetail("The prefix \"pg_\" is reserved for system schemas.")));
111 
112  /*
113  * If if_not_exists was given and the schema already exists, bail out.
114  * (Note: we needn't check this when not if_not_exists, because
115  * NamespaceCreate will complain anyway.) We could do this before making
116  * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
117  * creation-permission check first, we do likewise.
118  */
119  if (stmt->if_not_exists)
120  {
121  namespaceId = get_namespace_oid(schemaName, true);
122  if (OidIsValid(namespaceId))
123  {
124  /*
125  * If we are in an extension script, insist that the pre-existing
126  * object be a member of the extension, to avoid security risks.
127  */
128  ObjectAddressSet(address, NamespaceRelationId, namespaceId);
130 
131  /* OK to skip */
132  ereport(NOTICE,
133  (errcode(ERRCODE_DUPLICATE_SCHEMA),
134  errmsg("schema \"%s\" already exists, skipping",
135  schemaName)));
136  return InvalidOid;
137  }
138  }
139 
140  /*
141  * If the requested authorization is different from the current user,
142  * temporarily set the current user so that the object(s) will be created
143  * with the correct ownership.
144  *
145  * (The setting will be restored at the end of this routine, or in case of
146  * error, transaction abort will clean things up.)
147  */
148  if (saved_uid != owner_uid)
149  SetUserIdAndSecContext(owner_uid,
150  save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
151 
152  /* Create the schema's namespace */
153  namespaceId = NamespaceCreate(schemaName, owner_uid, false);
154 
155  /* Advance cmd counter to make the namespace visible */
157 
158  /*
159  * Prepend the new schema to the current search path.
160  *
161  * We use the equivalent of a function SET option to allow the setting to
162  * persist for exactly the duration of the schema creation. guc.c also
163  * takes care of undoing the setting on error.
164  */
165  save_nestlevel = NewGUCNestLevel();
166 
167  initStringInfo(&pathbuf);
168  appendStringInfoString(&pathbuf, quote_identifier(schemaName));
169 
170  while (scanner_isspace(*nsp))
171  nsp++;
172 
173  if (*nsp != '\0')
174  appendStringInfo(&pathbuf, ", %s", nsp);
175 
176  (void) set_config_option("search_path", pathbuf.data,
178  GUC_ACTION_SAVE, true, 0, false);
179 
180  /*
181  * Report the new schema to possibly interested event triggers. Note we
182  * must do this here and not in ProcessUtilitySlow because otherwise the
183  * objects created below are reported before the schema, which would be
184  * wrong.
185  */
186  ObjectAddressSet(address, NamespaceRelationId, namespaceId);
188  (Node *) stmt);
189 
190  /*
191  * Examine the list of commands embedded in the CREATE SCHEMA command, and
192  * reorganize them into a sequentially executable order with no forward
193  * references. Note that the result is still a list of raw parsetrees ---
194  * we cannot, in general, run parse analysis on one statement until we
195  * have actually executed the prior ones.
196  */
197  parsetree_list = transformCreateSchemaStmtElements(stmt->schemaElts,
198  schemaName);
199 
200  /*
201  * Execute each command contained in the CREATE SCHEMA. Since the grammar
202  * allows only utility commands in CREATE SCHEMA, there is no need to pass
203  * them through parse_analyze_*() or the rewriter; we can just hand them
204  * straight to ProcessUtility.
205  */
206  foreach(parsetree_item, parsetree_list)
207  {
208  Node *stmt = (Node *) lfirst(parsetree_item);
209  PlannedStmt *wrapper;
210 
211  /* need to make a wrapper PlannedStmt */
212  wrapper = makeNode(PlannedStmt);
213  wrapper->commandType = CMD_UTILITY;
214  wrapper->canSetTag = false;
215  wrapper->utilityStmt = stmt;
216  wrapper->stmt_location = stmt_location;
217  wrapper->stmt_len = stmt_len;
218 
219  /* do this step */
220  ProcessUtility(wrapper,
221  queryString,
222  false,
224  NULL,
225  NULL,
227  NULL);
228 
229  /* make sure later steps can see the object created here */
231  }
232 
233  /*
234  * Restore the GUC variable search_path we set above.
235  */
236  AtEOXact_GUC(true, save_nestlevel);
237 
238  /* Reset current user and security context */
239  SetUserIdAndSecContext(saved_uid, save_sec_context);
240 
241  return namespaceId;
242 }
243 
244 
245 /*
246  * Rename schema
247  */
249 RenameSchema(const char *oldname, const char *newname)
250 {
251  Oid nspOid;
252  HeapTuple tup;
253  Relation rel;
254  AclResult aclresult;
255  ObjectAddress address;
256  Form_pg_namespace nspform;
257 
258  rel = table_open(NamespaceRelationId, RowExclusiveLock);
259 
260  tup = SearchSysCacheCopy1(NAMESPACENAME, CStringGetDatum(oldname));
261  if (!HeapTupleIsValid(tup))
262  ereport(ERROR,
263  (errcode(ERRCODE_UNDEFINED_SCHEMA),
264  errmsg("schema \"%s\" does not exist", oldname)));
265 
266  nspform = (Form_pg_namespace) GETSTRUCT(tup);
267  nspOid = nspform->oid;
268 
269  /* make sure the new name doesn't exist */
270  if (OidIsValid(get_namespace_oid(newname, true)))
271  ereport(ERROR,
272  (errcode(ERRCODE_DUPLICATE_SCHEMA),
273  errmsg("schema \"%s\" already exists", newname)));
274 
275  /* must be owner */
276  if (!object_ownercheck(NamespaceRelationId, nspOid, GetUserId()))
278  oldname);
279 
280  /* must have CREATE privilege on database */
281  aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(), ACL_CREATE);
282  if (aclresult != ACLCHECK_OK)
283  aclcheck_error(aclresult, OBJECT_DATABASE,
285 
286  if (!allowSystemTableMods && IsReservedName(newname))
287  ereport(ERROR,
288  (errcode(ERRCODE_RESERVED_NAME),
289  errmsg("unacceptable schema name \"%s\"", newname),
290  errdetail("The prefix \"pg_\" is reserved for system schemas.")));
291 
292  /* rename */
293  namestrcpy(&nspform->nspname, newname);
294  CatalogTupleUpdate(rel, &tup->t_self, tup);
295 
296  InvokeObjectPostAlterHook(NamespaceRelationId, nspOid, 0);
297 
298  ObjectAddressSet(address, NamespaceRelationId, nspOid);
299 
300  table_close(rel, NoLock);
301  heap_freetuple(tup);
302 
303  return address;
304 }
305 
306 void
307 AlterSchemaOwner_oid(Oid schemaoid, Oid newOwnerId)
308 {
309  HeapTuple tup;
310  Relation rel;
311 
312  rel = table_open(NamespaceRelationId, RowExclusiveLock);
313 
314  tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(schemaoid));
315  if (!HeapTupleIsValid(tup))
316  elog(ERROR, "cache lookup failed for schema %u", schemaoid);
317 
318  AlterSchemaOwner_internal(tup, rel, newOwnerId);
319 
320  ReleaseSysCache(tup);
321 
323 }
324 
325 
326 /*
327  * Change schema owner
328  */
330 AlterSchemaOwner(const char *name, Oid newOwnerId)
331 {
332  Oid nspOid;
333  HeapTuple tup;
334  Relation rel;
335  ObjectAddress address;
336  Form_pg_namespace nspform;
337 
338  rel = table_open(NamespaceRelationId, RowExclusiveLock);
339 
340  tup = SearchSysCache1(NAMESPACENAME, CStringGetDatum(name));
341  if (!HeapTupleIsValid(tup))
342  ereport(ERROR,
343  (errcode(ERRCODE_UNDEFINED_SCHEMA),
344  errmsg("schema \"%s\" does not exist", name)));
345 
346  nspform = (Form_pg_namespace) GETSTRUCT(tup);
347  nspOid = nspform->oid;
348 
349  AlterSchemaOwner_internal(tup, rel, newOwnerId);
350 
351  ObjectAddressSet(address, NamespaceRelationId, nspOid);
352 
353  ReleaseSysCache(tup);
354 
356 
357  return address;
358 }
359 
360 static void
362 {
363  Form_pg_namespace nspForm;
364 
365  Assert(tup->t_tableOid == NamespaceRelationId);
366  Assert(RelationGetRelid(rel) == NamespaceRelationId);
367 
368  nspForm = (Form_pg_namespace) GETSTRUCT(tup);
369 
370  /*
371  * If the new owner is the same as the existing owner, consider the
372  * command to have succeeded. This is for dump restoration purposes.
373  */
374  if (nspForm->nspowner != newOwnerId)
375  {
376  Datum repl_val[Natts_pg_namespace];
377  bool repl_null[Natts_pg_namespace];
378  bool repl_repl[Natts_pg_namespace];
379  Acl *newAcl;
380  Datum aclDatum;
381  bool isNull;
382  HeapTuple newtuple;
383  AclResult aclresult;
384 
385  /* Otherwise, must be owner of the existing object */
386  if (!object_ownercheck(NamespaceRelationId, nspForm->oid, GetUserId()))
388  NameStr(nspForm->nspname));
389 
390  /* Must be able to become new owner */
391  check_can_set_role(GetUserId(), newOwnerId);
392 
393  /*
394  * must have create-schema rights
395  *
396  * NOTE: This is different from other alter-owner checks in that the
397  * current user is checked for create privileges instead of the
398  * destination owner. This is consistent with the CREATE case for
399  * schemas. Because superusers will always have this right, we need
400  * no special case for them.
401  */
402  aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, GetUserId(),
403  ACL_CREATE);
404  if (aclresult != ACLCHECK_OK)
405  aclcheck_error(aclresult, OBJECT_DATABASE,
407 
408  memset(repl_null, false, sizeof(repl_null));
409  memset(repl_repl, false, sizeof(repl_repl));
410 
411  repl_repl[Anum_pg_namespace_nspowner - 1] = true;
412  repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
413 
414  /*
415  * Determine the modified ACL for the new owner. This is only
416  * necessary when the ACL is non-null.
417  */
418  aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
419  Anum_pg_namespace_nspacl,
420  &isNull);
421  if (!isNull)
422  {
423  newAcl = aclnewowner(DatumGetAclP(aclDatum),
424  nspForm->nspowner, newOwnerId);
425  repl_repl[Anum_pg_namespace_nspacl - 1] = true;
426  repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
427  }
428 
429  newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
430 
431  CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
432 
433  heap_freetuple(newtuple);
434 
435  /* Update owner dependency reference */
436  changeDependencyOnOwner(NamespaceRelationId, nspForm->oid,
437  newOwnerId);
438  }
439 
440  InvokeObjectPostAlterHook(NamespaceRelationId,
441  nspForm->oid, 0);
442 }
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1103
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5325
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5588
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
#define DatumGetAclP(X)
Definition: acl.h:120
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2703
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3891
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4145
#define NameStr(name)
Definition: c.h:749
#define Assert(condition)
Definition: c.h:861
#define OidIsValid(objectId)
Definition: c.h:778
bool IsReservedName(const char *name)
Definition: catalog.c:247
char * get_database_name(Oid dbid)
Definition: dbcommands.c:3187
DestReceiver * None_Receiver
Definition: dest.c:96
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
void EventTriggerCollectSimpleCommand(ObjectAddress address, ObjectAddress secondaryObject, Node *parsetree)
bool allowSystemTableMods
Definition: globals.c:129
Oid MyDatabaseId
Definition: globals.c:93
int NewGUCNestLevel(void)
Definition: guc.c:2234
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2261
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition: guc.c:3342
@ GUC_ACTION_SAVE
Definition: guc.h:201
@ PGC_S_SESSION
Definition: guc.h:122
@ PGC_USERSET
Definition: guc.h:75
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1209
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
#define stmt
Definition: indent_codes.h:59
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
char * pstrdup(const char *in)
Definition: mcxt.c:1696
#define SECURITY_LOCAL_USERID_CHANGE
Definition: miscadmin.h:311
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:635
Oid GetUserId(void)
Definition: miscinit.c:514
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:642
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid get_namespace_oid(const char *nspname, bool missing_ok)
Definition: namespace.c:3520
char * namespace_search_path
Definition: namespace.c:211
@ CMD_UTILITY
Definition: nodes.h:270
#define makeNode(_type_)
Definition: nodes.h:155
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
List * transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
@ OBJECT_SCHEMA
Definition: parsenodes.h:2293
@ OBJECT_DATABASE
Definition: parsenodes.h:2266
#define ACL_CREATE
Definition: parsenodes.h:85
NameData rolname
Definition: pg_authid.h:34
FormData_pg_authid * Form_pg_authid
Definition: pg_authid.h:56
void checkMembershipInCurrentExtension(const ObjectAddress *object)
Definition: pg_depend.c:259
#define lfirst(lc)
Definition: pg_list.h:172
Oid NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
Definition: pg_namespace.c:43
FormData_pg_namespace * Form_pg_namespace
Definition: pg_namespace.h:52
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:316
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelid(relation)
Definition: rel.h:505
#define RelationGetDescr(relation)
Definition: rel.h:531
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:12840
bool scanner_isspace(char ch)
Definition: scansup.c:117
Oid CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString, int stmt_location, int stmt_len)
Definition: schemacmds.c:52
void AlterSchemaOwner_oid(Oid schemaoid, Oid newOwnerId)
Definition: schemacmds.c:307
ObjectAddress AlterSchemaOwner(const char *name, Oid newOwnerId)
Definition: schemacmds.c:330
ObjectAddress RenameSchema(const char *oldname, const char *newname)
Definition: schemacmds.c:249
static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
Definition: schemacmds.c:361
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:97
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
ItemPointerData t_self
Definition: htup.h:65
Oid t_tableOid
Definition: htup.h:66
Definition: pg_list.h:54
Definition: nodes.h:129
bool canSetTag
Definition: plannodes.h:60
ParseLoc stmt_len
Definition: plannodes.h:99
ParseLoc stmt_location
Definition: plannodes.h:98
CmdType commandType
Definition: plannodes.h:52
Node * utilityStmt
Definition: plannodes.h:95
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:596
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:91
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:499
@ PROCESS_UTILITY_SUBCOMMAND
Definition: utility.h:26
const char * name
void CommandCounterIncrement(void)
Definition: xact.c:1099