1/*-------------------------------------------------------------------------
4 * schema creation/manipulation commands
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/commands/schemacmds.c
13 *-------------------------------------------------------------------------
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
53 int stmt_location,
int stmt_len)
55 const char *schemaName =
stmt->schemaname;
71 * Who is supposed to own the new schema?
76 owner_uid = saved_uid;
78 /* fill schema name with the user name if not specified */
85 elog(
ERROR,
"cache lookup failed for role %u", owner_uid);
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.
105 /* Additional check to protect reserved schema names */
108 (
errcode(ERRCODE_RESERVED_NAME),
109 errmsg(
"unacceptable schema name \"%s\"", schemaName),
110 errdetail(
"The prefix \"pg_\" is reserved for system schemas.")));
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.
119 if (
stmt->if_not_exists)
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.
133 (
errcode(ERRCODE_DUPLICATE_SCHEMA),
134 errmsg(
"schema \"%s\" already exists, skipping",
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.
145 * (The setting will be restored at the end of this routine, or in case of
146 * error, transaction abort will clean things up.)
148 if (saved_uid != owner_uid)
152 /* Create the schema's namespace */
155 /* Advance cmd counter to make the namespace visible */
159 * Prepend the new schema to the current search path.
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.
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
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.
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.
206 foreach(parsetree_item, parsetree_list)
211 /* need to make a wrapper PlannedStmt */
230 /* make sure later steps can see the object created here */
235 * Restore the GUC variable search_path we set above.
239 /* Reset current user and security context */
264 (
errcode(ERRCODE_UNDEFINED_SCHEMA),
265 errmsg(
"schema \"%s\" does not exist", oldname)));
268 nspOid = nspform->oid;
270 /* make sure the new name doesn't exist */
273 (
errcode(ERRCODE_DUPLICATE_SCHEMA),
274 errmsg(
"schema \"%s\" already exists", newname)));
281 /* must have CREATE privilege on database */
289 (
errcode(ERRCODE_RESERVED_NAME),
290 errmsg(
"unacceptable schema name \"%s\"", newname),
291 errdetail(
"The prefix \"pg_\" is reserved for system schemas.")));
317 elog(
ERROR,
"cache lookup failed for schema %u", schemaoid);
328 * Change schema owner
344 (
errcode(ERRCODE_UNDEFINED_SCHEMA),
345 errmsg(
"schema \"%s\" does not exist",
name)));
348 nspOid = nspform->oid;
372 * If the new owner is the same as the existing owner, consider the
373 * command to have succeeded. This is for dump restoration purposes.
375 if (nspForm->nspowner != newOwnerId)
377 Datum repl_val[Natts_pg_namespace];
378 bool repl_null[Natts_pg_namespace];
379 bool repl_repl[Natts_pg_namespace];
386 /* Otherwise, must be owner of the existing object */
391 /* Must be able to become new owner */
395 * must have create-schema rights
397 * NOTE: This is different from other alter-owner checks in that the
398 * current user is checked for create privileges instead of the
399 * destination owner. This is consistent with the CREATE case for
400 * schemas. Because superusers will always have this right, we need
401 * no special case for them.
409 memset(repl_null,
false,
sizeof(repl_null));
410 memset(repl_repl,
false,
sizeof(repl_repl));
412 repl_repl[Anum_pg_namespace_nspowner - 1] =
true;
416 * Determine the modified ACL for the new owner. This is only
417 * necessary when the ACL is non-null.
420 Anum_pg_namespace_nspacl,
425 nspForm->nspowner, newOwnerId);
426 repl_repl[Anum_pg_namespace_nspacl - 1] =
true;
436 /* Update owner dependency reference */
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
void check_can_set_role(Oid member, Oid role)
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
#define OidIsValid(objectId)
bool IsReservedName(const char *name)
DestReceiver * None_Receiver
int errdetail(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
void EventTriggerCollectSimpleCommand(ObjectAddress address, ObjectAddress secondaryObject, Node *parsetree)
bool allowSystemTableMods
int NewGUCNestLevel(void)
void AtEOXact_GUC(bool isCommit, int nestLevel)
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Assert(PointerIsAligned(start, uint64))
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
void heap_freetuple(HeapTuple htup)
#define HeapTupleIsValid(tuple)
static void * GETSTRUCT(const HeapTupleData *tuple)
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
char * get_database_name(Oid dbid)
char * pstrdup(const char *in)
#define SECURITY_LOCAL_USERID_CHANGE
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
void SetUserIdAndSecContext(Oid userid, int sec_context)
void namestrcpy(Name name, const char *str)
Oid get_namespace_oid(const char *nspname, bool missing_ok)
char * namespace_search_path
#define InvokeObjectPostAlterHook(classId, objectId, subId)
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
List * transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
FormData_pg_authid * Form_pg_authid
void checkMembershipInCurrentExtension(const ObjectAddress *object)
Oid NamespaceCreate(const char *nspName, Oid ownerId, bool isTemp)
FormData_pg_namespace * Form_pg_namespace
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
static Datum PointerGetDatum(const void *X)
static Datum ObjectIdGetDatum(Oid X)
static Datum CStringGetDatum(const char *X)
#define RelationGetRelid(relation)
#define RelationGetDescr(relation)
const char * quote_identifier(const char *ident)
bool scanner_isspace(char ch)
Oid CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString, int stmt_location, int stmt_len)
void AlterSchemaOwner_oid(Oid schemaoid, Oid newOwnerId)
ObjectAddress AlterSchemaOwner(const char *name, Oid newOwnerId)
ObjectAddress RenameSchema(const char *oldname, const char *newname)
static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
void appendStringInfo(StringInfo str, const char *fmt,...)
void appendStringInfoString(StringInfo str, const char *s)
void initStringInfo(StringInfo str)
PlannedStmtOrigin planOrigin
void ReleaseSysCache(HeapTuple tuple)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
#define SearchSysCacheCopy1(cacheId, key1)
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
@ PROCESS_UTILITY_SUBCOMMAND
void CommandCounterIncrement(void)