Header And Logo

PostgreSQL
| The world's most advanced open source database.

schemacmds.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * schemacmds.c
00004  *    schema creation/manipulation commands
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/commands/schemacmds.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 #include "postgres.h"
00016 
00017 #include "access/htup_details.h"
00018 #include "access/heapam.h"
00019 #include "access/xact.h"
00020 #include "catalog/catalog.h"
00021 #include "catalog/dependency.h"
00022 #include "catalog/indexing.h"
00023 #include "catalog/namespace.h"
00024 #include "catalog/objectaccess.h"
00025 #include "catalog/pg_namespace.h"
00026 #include "commands/dbcommands.h"
00027 #include "commands/schemacmds.h"
00028 #include "miscadmin.h"
00029 #include "parser/parse_utilcmd.h"
00030 #include "tcop/utility.h"
00031 #include "utils/acl.h"
00032 #include "utils/builtins.h"
00033 #include "utils/rel.h"
00034 #include "utils/syscache.h"
00035 
00036 
00037 static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
00038 
00039 /*
00040  * CREATE SCHEMA
00041  */
00042 Oid
00043 CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
00044 {
00045     const char *schemaName = stmt->schemaname;
00046     const char *authId = stmt->authid;
00047     Oid         namespaceId;
00048     OverrideSearchPath *overridePath;
00049     List       *parsetree_list;
00050     ListCell   *parsetree_item;
00051     Oid         owner_uid;
00052     Oid         saved_uid;
00053     int         save_sec_context;
00054     AclResult   aclresult;
00055 
00056     GetUserIdAndSecContext(&saved_uid, &save_sec_context);
00057 
00058     /*
00059      * Who is supposed to own the new schema?
00060      */
00061     if (authId)
00062         owner_uid = get_role_oid(authId, false);
00063     else
00064         owner_uid = saved_uid;
00065 
00066     /*
00067      * To create a schema, must have schema-create privilege on the current
00068      * database and must be able to become the target role (this does not
00069      * imply that the target role itself must have create-schema privilege).
00070      * The latter provision guards against "giveaway" attacks.  Note that a
00071      * superuser will always have both of these privileges a fortiori.
00072      */
00073     aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
00074     if (aclresult != ACLCHECK_OK)
00075         aclcheck_error(aclresult, ACL_KIND_DATABASE,
00076                        get_database_name(MyDatabaseId));
00077 
00078     check_is_member_of_role(saved_uid, owner_uid);
00079 
00080     /* Additional check to protect reserved schema names */
00081     if (!allowSystemTableMods && IsReservedName(schemaName))
00082         ereport(ERROR,
00083                 (errcode(ERRCODE_RESERVED_NAME),
00084                  errmsg("unacceptable schema name \"%s\"", schemaName),
00085            errdetail("The prefix \"pg_\" is reserved for system schemas.")));
00086 
00087     /*
00088      * If if_not_exists was given and the schema already exists, bail out.
00089      * (Note: we needn't check this when not if_not_exists, because
00090      * NamespaceCreate will complain anyway.)  We could do this before making
00091      * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
00092      * creation-permission check first, we do likewise.
00093      */
00094     if (stmt->if_not_exists &&
00095         SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName)))
00096     {
00097         ereport(NOTICE,
00098                 (errcode(ERRCODE_DUPLICATE_SCHEMA),
00099                  errmsg("schema \"%s\" already exists, skipping",
00100                         schemaName)));
00101         return InvalidOid;
00102     }
00103 
00104     /*
00105      * If the requested authorization is different from the current user,
00106      * temporarily set the current user so that the object(s) will be created
00107      * with the correct ownership.
00108      *
00109      * (The setting will be restored at the end of this routine, or in case of
00110      * error, transaction abort will clean things up.)
00111      */
00112     if (saved_uid != owner_uid)
00113         SetUserIdAndSecContext(owner_uid,
00114                             save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
00115 
00116     /* Create the schema's namespace */
00117     namespaceId = NamespaceCreate(schemaName, owner_uid, false);
00118 
00119     /* Advance cmd counter to make the namespace visible */
00120     CommandCounterIncrement();
00121 
00122     /*
00123      * Temporarily make the new namespace be the front of the search path, as
00124      * well as the default creation target namespace.  This will be undone at
00125      * the end of this routine, or upon error.
00126      */
00127     overridePath = GetOverrideSearchPath(CurrentMemoryContext);
00128     overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
00129     /* XXX should we clear overridePath->useTemp? */
00130     PushOverrideSearchPath(overridePath);
00131 
00132     /*
00133      * Examine the list of commands embedded in the CREATE SCHEMA command, and
00134      * reorganize them into a sequentially executable order with no forward
00135      * references.  Note that the result is still a list of raw parsetrees ---
00136      * we cannot, in general, run parse analysis on one statement until we
00137      * have actually executed the prior ones.
00138      */
00139     parsetree_list = transformCreateSchemaStmt(stmt);
00140 
00141     /*
00142      * Execute each command contained in the CREATE SCHEMA.  Since the grammar
00143      * allows only utility commands in CREATE SCHEMA, there is no need to pass
00144      * them through parse_analyze() or the rewriter; we can just hand them
00145      * straight to ProcessUtility.
00146      */
00147     foreach(parsetree_item, parsetree_list)
00148     {
00149         Node       *stmt = (Node *) lfirst(parsetree_item);
00150 
00151         /* do this step */
00152         ProcessUtility(stmt,
00153                        queryString,
00154                        PROCESS_UTILITY_SUBCOMMAND,
00155                        NULL,
00156                        None_Receiver,
00157                        NULL);
00158         /* make sure later steps can see the object created here */
00159         CommandCounterIncrement();
00160     }
00161 
00162     /* Reset search path to normal state */
00163     PopOverrideSearchPath();
00164 
00165     /* Reset current user and security context */
00166     SetUserIdAndSecContext(saved_uid, save_sec_context);
00167 
00168     return namespaceId;
00169 }
00170 
00171 /*
00172  * Guts of schema deletion.
00173  */
00174 void
00175 RemoveSchemaById(Oid schemaOid)
00176 {
00177     Relation    relation;
00178     HeapTuple   tup;
00179 
00180     relation = heap_open(NamespaceRelationId, RowExclusiveLock);
00181 
00182     tup = SearchSysCache1(NAMESPACEOID,
00183                           ObjectIdGetDatum(schemaOid));
00184     if (!HeapTupleIsValid(tup)) /* should not happen */
00185         elog(ERROR, "cache lookup failed for namespace %u", schemaOid);
00186 
00187     simple_heap_delete(relation, &tup->t_self);
00188 
00189     ReleaseSysCache(tup);
00190 
00191     heap_close(relation, RowExclusiveLock);
00192 }
00193 
00194 
00195 /*
00196  * Rename schema
00197  */
00198 Oid
00199 RenameSchema(const char *oldname, const char *newname)
00200 {
00201     Oid         nspOid;
00202     HeapTuple   tup;
00203     Relation    rel;
00204     AclResult   aclresult;
00205 
00206     rel = heap_open(NamespaceRelationId, RowExclusiveLock);
00207 
00208     tup = SearchSysCacheCopy1(NAMESPACENAME, CStringGetDatum(oldname));
00209     if (!HeapTupleIsValid(tup))
00210         ereport(ERROR,
00211                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
00212                  errmsg("schema \"%s\" does not exist", oldname)));
00213 
00214     nspOid = HeapTupleGetOid(tup);
00215 
00216     /* make sure the new name doesn't exist */
00217     if (OidIsValid(get_namespace_oid(newname, true)))
00218         ereport(ERROR,
00219                 (errcode(ERRCODE_DUPLICATE_SCHEMA),
00220                  errmsg("schema \"%s\" already exists", newname)));
00221 
00222     /* must be owner */
00223     if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
00224         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
00225                        oldname);
00226 
00227     /* must have CREATE privilege on database */
00228     aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
00229     if (aclresult != ACLCHECK_OK)
00230         aclcheck_error(aclresult, ACL_KIND_DATABASE,
00231                        get_database_name(MyDatabaseId));
00232 
00233     if (!allowSystemTableMods && IsReservedName(newname))
00234         ereport(ERROR,
00235                 (errcode(ERRCODE_RESERVED_NAME),
00236                  errmsg("unacceptable schema name \"%s\"", newname),
00237            errdetail("The prefix \"pg_\" is reserved for system schemas.")));
00238 
00239     /* rename */
00240     namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
00241     simple_heap_update(rel, &tup->t_self, tup);
00242     CatalogUpdateIndexes(rel, tup);
00243 
00244     InvokeObjectPostAlterHook(NamespaceRelationId, HeapTupleGetOid(tup), 0);
00245 
00246     heap_close(rel, NoLock);
00247     heap_freetuple(tup);
00248 
00249     return nspOid;
00250 }
00251 
00252 void
00253 AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
00254 {
00255     HeapTuple   tup;
00256     Relation    rel;
00257 
00258     rel = heap_open(NamespaceRelationId, RowExclusiveLock);
00259 
00260     tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(oid));
00261     if (!HeapTupleIsValid(tup))
00262         elog(ERROR, "cache lookup failed for schema %u", oid);
00263 
00264     AlterSchemaOwner_internal(tup, rel, newOwnerId);
00265 
00266     ReleaseSysCache(tup);
00267 
00268     heap_close(rel, RowExclusiveLock);
00269 }
00270 
00271 
00272 /*
00273  * Change schema owner
00274  */
00275 Oid
00276 AlterSchemaOwner(const char *name, Oid newOwnerId)
00277 {
00278     Oid         nspOid;
00279     HeapTuple   tup;
00280     Relation    rel;
00281 
00282     rel = heap_open(NamespaceRelationId, RowExclusiveLock);
00283 
00284     tup = SearchSysCache1(NAMESPACENAME, CStringGetDatum(name));
00285     if (!HeapTupleIsValid(tup))
00286         ereport(ERROR,
00287                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
00288                  errmsg("schema \"%s\" does not exist", name)));
00289 
00290     nspOid = HeapTupleGetOid(tup);
00291 
00292     AlterSchemaOwner_internal(tup, rel, newOwnerId);
00293 
00294     ReleaseSysCache(tup);
00295 
00296     heap_close(rel, RowExclusiveLock);
00297 
00298     return nspOid;
00299 }
00300 
00301 static void
00302 AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
00303 {
00304     Form_pg_namespace nspForm;
00305 
00306     Assert(tup->t_tableOid == NamespaceRelationId);
00307     Assert(RelationGetRelid(rel) == NamespaceRelationId);
00308 
00309     nspForm = (Form_pg_namespace) GETSTRUCT(tup);
00310 
00311     /*
00312      * If the new owner is the same as the existing owner, consider the
00313      * command to have succeeded.  This is for dump restoration purposes.
00314      */
00315     if (nspForm->nspowner != newOwnerId)
00316     {
00317         Datum       repl_val[Natts_pg_namespace];
00318         bool        repl_null[Natts_pg_namespace];
00319         bool        repl_repl[Natts_pg_namespace];
00320         Acl        *newAcl;
00321         Datum       aclDatum;
00322         bool        isNull;
00323         HeapTuple   newtuple;
00324         AclResult   aclresult;
00325 
00326         /* Otherwise, must be owner of the existing object */
00327         if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
00328             aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
00329                            NameStr(nspForm->nspname));
00330 
00331         /* Must be able to become new owner */
00332         check_is_member_of_role(GetUserId(), newOwnerId);
00333 
00334         /*
00335          * must have create-schema rights
00336          *
00337          * NOTE: This is different from other alter-owner checks in that the
00338          * current user is checked for create privileges instead of the
00339          * destination owner.  This is consistent with the CREATE case for
00340          * schemas.  Because superusers will always have this right, we need
00341          * no special case for them.
00342          */
00343         aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
00344                                          ACL_CREATE);
00345         if (aclresult != ACLCHECK_OK)
00346             aclcheck_error(aclresult, ACL_KIND_DATABASE,
00347                            get_database_name(MyDatabaseId));
00348 
00349         memset(repl_null, false, sizeof(repl_null));
00350         memset(repl_repl, false, sizeof(repl_repl));
00351 
00352         repl_repl[Anum_pg_namespace_nspowner - 1] = true;
00353         repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
00354 
00355         /*
00356          * Determine the modified ACL for the new owner.  This is only
00357          * necessary when the ACL is non-null.
00358          */
00359         aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
00360                                    Anum_pg_namespace_nspacl,
00361                                    &isNull);
00362         if (!isNull)
00363         {
00364             newAcl = aclnewowner(DatumGetAclP(aclDatum),
00365                                  nspForm->nspowner, newOwnerId);
00366             repl_repl[Anum_pg_namespace_nspacl - 1] = true;
00367             repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
00368         }
00369 
00370         newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
00371 
00372         simple_heap_update(rel, &newtuple->t_self, newtuple);
00373         CatalogUpdateIndexes(rel, newtuple);
00374 
00375         heap_freetuple(newtuple);
00376 
00377         /* Update owner dependency reference */
00378         changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
00379                                 newOwnerId);
00380     }
00381 
00382     InvokeObjectPostAlterHook(NamespaceRelationId,
00383                               HeapTupleGetOid(tup), 0);
00384 }