LCOV - code coverage report
Current view: top level - src/backend/commands - schemacmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 102 127 80.3 %
Date: 2017-09-29 15:12:54 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * schemacmds.c
       4             :  *    schema creation/manipulation commands
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, 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/heapam.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/pg_authid.h"
      25             : #include "catalog/objectaccess.h"
      26             : #include "catalog/pg_namespace.h"
      27             : #include "commands/dbcommands.h"
      28             : #include "commands/event_trigger.h"
      29             : #include "commands/schemacmds.h"
      30             : #include "miscadmin.h"
      31             : #include "parser/parse_utilcmd.h"
      32             : #include "tcop/utility.h"
      33             : #include "utils/acl.h"
      34             : #include "utils/builtins.h"
      35             : #include "utils/rel.h"
      36             : #include "utils/syscache.h"
      37             : 
      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
      51          62 : CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
      52             :                     int stmt_location, int stmt_len)
      53             : {
      54          62 :     const char *schemaName = stmt->schemaname;
      55             :     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             : 
      65          62 :     GetUserIdAndSecContext(&saved_uid, &save_sec_context);
      66             : 
      67             :     /*
      68             :      * Who is supposed to own the new schema?
      69             :      */
      70          62 :     if (stmt->authrole)
      71          20 :         owner_uid = get_rolespec_oid(stmt->authrole, false);
      72             :     else
      73          42 :         owner_uid = saved_uid;
      74             : 
      75             :     /* fill schema name with the user name if not specified */
      76          56 :     if (!schemaName)
      77             :     {
      78             :         HeapTuple   tuple;
      79             : 
      80           0 :         tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(owner_uid));
      81           0 :         if (!HeapTupleIsValid(tuple))
      82           0 :             elog(ERROR, "cache lookup failed for role %u", owner_uid);
      83           0 :         schemaName =
      84           0 :             pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
      85           0 :         ReleaseSysCache(tuple);
      86             :     }
      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             :      */
      95          56 :     aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
      96          56 :     if (aclresult != ACLCHECK_OK)
      97           0 :         aclcheck_error(aclresult, ACL_KIND_DATABASE,
      98           0 :                        get_database_name(MyDatabaseId));
      99             : 
     100          56 :     check_is_member_of_role(saved_uid, owner_uid);
     101             : 
     102             :     /* Additional check to protect reserved schema names */
     103          56 :     if (!allowSystemTableMods && IsReservedName(schemaName))
     104           0 :         ereport(ERROR,
     105             :                 (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             :      */
     116          62 :     if (stmt->if_not_exists &&
     117           6 :         SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName)))
     118             :     {
     119           6 :         ereport(NOTICE,
     120             :                 (errcode(ERRCODE_DUPLICATE_SCHEMA),
     121             :                  errmsg("schema \"%s\" already exists, skipping",
     122             :                         schemaName)));
     123           6 :         return InvalidOid;
     124             :     }
     125             : 
     126             :     /*
     127             :      * If the requested authorization is different from the current user,
     128             :      * temporarily set the current user so that the object(s) will be created
     129             :      * with the correct ownership.
     130             :      *
     131             :      * (The setting will be restored at the end of this routine, or in case of
     132             :      * error, transaction abort will clean things up.)
     133             :      */
     134          50 :     if (saved_uid != owner_uid)
     135           8 :         SetUserIdAndSecContext(owner_uid,
     136             :                                save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
     137             : 
     138             :     /* Create the schema's namespace */
     139          50 :     namespaceId = NamespaceCreate(schemaName, owner_uid, false);
     140             : 
     141             :     /* Advance cmd counter to make the namespace visible */
     142          49 :     CommandCounterIncrement();
     143             : 
     144             :     /*
     145             :      * Temporarily make the new namespace be the front of the search path, as
     146             :      * well as the default creation target namespace.  This will be undone at
     147             :      * the end of this routine, or upon error.
     148             :      */
     149          49 :     overridePath = GetOverrideSearchPath(CurrentMemoryContext);
     150          49 :     overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
     151             :     /* XXX should we clear overridePath->useTemp? */
     152          49 :     PushOverrideSearchPath(overridePath);
     153             : 
     154             :     /*
     155             :      * Report the new schema to possibly interested event triggers.  Note we
     156             :      * must do this here and not in ProcessUtilitySlow because otherwise the
     157             :      * objects created below are reported before the schema, which would be
     158             :      * wrong.
     159             :      */
     160          49 :     ObjectAddressSet(address, NamespaceRelationId, namespaceId);
     161          49 :     EventTriggerCollectSimpleCommand(address, InvalidObjectAddress,
     162             :                                      (Node *) stmt);
     163             : 
     164             :     /*
     165             :      * Examine the list of commands embedded in the CREATE SCHEMA command, and
     166             :      * reorganize them into a sequentially executable order with no forward
     167             :      * references.  Note that the result is still a list of raw parsetrees ---
     168             :      * we cannot, in general, run parse analysis on one statement until we
     169             :      * have actually executed the prior ones.
     170             :      */
     171          49 :     parsetree_list = transformCreateSchemaStmt(stmt);
     172             : 
     173             :     /*
     174             :      * Execute each command contained in the CREATE SCHEMA.  Since the grammar
     175             :      * allows only utility commands in CREATE SCHEMA, there is no need to pass
     176             :      * them through parse_analyze() or the rewriter; we can just hand them
     177             :      * straight to ProcessUtility.
     178             :      */
     179          59 :     foreach(parsetree_item, parsetree_list)
     180             :     {
     181          11 :         Node       *stmt = (Node *) lfirst(parsetree_item);
     182             :         PlannedStmt *wrapper;
     183             : 
     184             :         /* need to make a wrapper PlannedStmt */
     185          11 :         wrapper = makeNode(PlannedStmt);
     186          11 :         wrapper->commandType = CMD_UTILITY;
     187          11 :         wrapper->canSetTag = false;
     188          11 :         wrapper->utilityStmt = stmt;
     189          11 :         wrapper->stmt_location = stmt_location;
     190          11 :         wrapper->stmt_len = stmt_len;
     191             : 
     192             :         /* do this step */
     193          11 :         ProcessUtility(wrapper,
     194             :                        queryString,
     195             :                        PROCESS_UTILITY_SUBCOMMAND,
     196             :                        NULL,
     197             :                        NULL,
     198             :                        None_Receiver,
     199             :                        NULL);
     200             : 
     201             :         /* make sure later steps can see the object created here */
     202          10 :         CommandCounterIncrement();
     203             :     }
     204             : 
     205             :     /* Reset search path to normal state */
     206          48 :     PopOverrideSearchPath();
     207             : 
     208             :     /* Reset current user and security context */
     209          48 :     SetUserIdAndSecContext(saved_uid, save_sec_context);
     210             : 
     211          48 :     return namespaceId;
     212             : }
     213             : 
     214             : /*
     215             :  * Guts of schema deletion.
     216             :  */
     217             : void
     218          45 : RemoveSchemaById(Oid schemaOid)
     219             : {
     220             :     Relation    relation;
     221             :     HeapTuple   tup;
     222             : 
     223          45 :     relation = heap_open(NamespaceRelationId, RowExclusiveLock);
     224             : 
     225          45 :     tup = SearchSysCache1(NAMESPACEOID,
     226             :                           ObjectIdGetDatum(schemaOid));
     227          45 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     228           0 :         elog(ERROR, "cache lookup failed for namespace %u", schemaOid);
     229             : 
     230          45 :     CatalogTupleDelete(relation, &tup->t_self);
     231             : 
     232          45 :     ReleaseSysCache(tup);
     233             : 
     234          45 :     heap_close(relation, RowExclusiveLock);
     235          45 : }
     236             : 
     237             : 
     238             : /*
     239             :  * Rename schema
     240             :  */
     241             : ObjectAddress
     242           1 : RenameSchema(const char *oldname, const char *newname)
     243             : {
     244             :     Oid         nspOid;
     245             :     HeapTuple   tup;
     246             :     Relation    rel;
     247             :     AclResult   aclresult;
     248             :     ObjectAddress address;
     249             : 
     250           1 :     rel = heap_open(NamespaceRelationId, RowExclusiveLock);
     251             : 
     252           1 :     tup = SearchSysCacheCopy1(NAMESPACENAME, CStringGetDatum(oldname));
     253           1 :     if (!HeapTupleIsValid(tup))
     254           0 :         ereport(ERROR,
     255             :                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
     256             :                  errmsg("schema \"%s\" does not exist", oldname)));
     257             : 
     258           1 :     nspOid = HeapTupleGetOid(tup);
     259             : 
     260             :     /* make sure the new name doesn't exist */
     261           1 :     if (OidIsValid(get_namespace_oid(newname, true)))
     262           0 :         ereport(ERROR,
     263             :                 (errcode(ERRCODE_DUPLICATE_SCHEMA),
     264             :                  errmsg("schema \"%s\" already exists", newname)));
     265             : 
     266             :     /* must be owner */
     267           1 :     if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
     268           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
     269             :                        oldname);
     270             : 
     271             :     /* must have CREATE privilege on database */
     272           1 :     aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
     273           1 :     if (aclresult != ACLCHECK_OK)
     274           0 :         aclcheck_error(aclresult, ACL_KIND_DATABASE,
     275           0 :                        get_database_name(MyDatabaseId));
     276             : 
     277           1 :     if (!allowSystemTableMods && IsReservedName(newname))
     278           0 :         ereport(ERROR,
     279             :                 (errcode(ERRCODE_RESERVED_NAME),
     280             :                  errmsg("unacceptable schema name \"%s\"", newname),
     281             :                  errdetail("The prefix \"pg_\" is reserved for system schemas.")));
     282             : 
     283             :     /* rename */
     284           1 :     namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
     285           1 :     CatalogTupleUpdate(rel, &tup->t_self, tup);
     286             : 
     287           1 :     InvokeObjectPostAlterHook(NamespaceRelationId, HeapTupleGetOid(tup), 0);
     288             : 
     289           1 :     ObjectAddressSet(address, NamespaceRelationId, nspOid);
     290             : 
     291           1 :     heap_close(rel, NoLock);
     292           1 :     heap_freetuple(tup);
     293             : 
     294           1 :     return address;
     295             : }
     296             : 
     297             : void
     298           1 : AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
     299             : {
     300             :     HeapTuple   tup;
     301             :     Relation    rel;
     302             : 
     303           1 :     rel = heap_open(NamespaceRelationId, RowExclusiveLock);
     304             : 
     305           1 :     tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(oid));
     306           1 :     if (!HeapTupleIsValid(tup))
     307           0 :         elog(ERROR, "cache lookup failed for schema %u", oid);
     308             : 
     309           1 :     AlterSchemaOwner_internal(tup, rel, newOwnerId);
     310             : 
     311           1 :     ReleaseSysCache(tup);
     312             : 
     313           1 :     heap_close(rel, RowExclusiveLock);
     314           1 : }
     315             : 
     316             : 
     317             : /*
     318             :  * Change schema owner
     319             :  */
     320             : ObjectAddress
     321           1 : AlterSchemaOwner(const char *name, Oid newOwnerId)
     322             : {
     323             :     Oid         nspOid;
     324             :     HeapTuple   tup;
     325             :     Relation    rel;
     326             :     ObjectAddress address;
     327             : 
     328           1 :     rel = heap_open(NamespaceRelationId, RowExclusiveLock);
     329             : 
     330           1 :     tup = SearchSysCache1(NAMESPACENAME, CStringGetDatum(name));
     331           1 :     if (!HeapTupleIsValid(tup))
     332           0 :         ereport(ERROR,
     333             :                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
     334             :                  errmsg("schema \"%s\" does not exist", name)));
     335             : 
     336           1 :     nspOid = HeapTupleGetOid(tup);
     337             : 
     338           1 :     AlterSchemaOwner_internal(tup, rel, newOwnerId);
     339             : 
     340           1 :     ObjectAddressSet(address, NamespaceRelationId, nspOid);
     341             : 
     342           1 :     ReleaseSysCache(tup);
     343             : 
     344           1 :     heap_close(rel, RowExclusiveLock);
     345             : 
     346           1 :     return address;
     347             : }
     348             : 
     349             : static void
     350           2 : AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
     351             : {
     352             :     Form_pg_namespace nspForm;
     353             : 
     354           2 :     Assert(tup->t_tableOid == NamespaceRelationId);
     355           2 :     Assert(RelationGetRelid(rel) == NamespaceRelationId);
     356             : 
     357           2 :     nspForm = (Form_pg_namespace) GETSTRUCT(tup);
     358             : 
     359             :     /*
     360             :      * If the new owner is the same as the existing owner, consider the
     361             :      * command to have succeeded.  This is for dump restoration purposes.
     362             :      */
     363           2 :     if (nspForm->nspowner != newOwnerId)
     364             :     {
     365             :         Datum       repl_val[Natts_pg_namespace];
     366             :         bool        repl_null[Natts_pg_namespace];
     367             :         bool        repl_repl[Natts_pg_namespace];
     368             :         Acl        *newAcl;
     369             :         Datum       aclDatum;
     370             :         bool        isNull;
     371             :         HeapTuple   newtuple;
     372             :         AclResult   aclresult;
     373             : 
     374             :         /* Otherwise, must be owner of the existing object */
     375           2 :         if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
     376           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
     377           0 :                            NameStr(nspForm->nspname));
     378             : 
     379             :         /* Must be able to become new owner */
     380           2 :         check_is_member_of_role(GetUserId(), newOwnerId);
     381             : 
     382             :         /*
     383             :          * must have create-schema rights
     384             :          *
     385             :          * NOTE: This is different from other alter-owner checks in that the
     386             :          * current user is checked for create privileges instead of the
     387             :          * destination owner.  This is consistent with the CREATE case for
     388             :          * schemas.  Because superusers will always have this right, we need
     389             :          * no special case for them.
     390             :          */
     391           2 :         aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
     392             :                                          ACL_CREATE);
     393           2 :         if (aclresult != ACLCHECK_OK)
     394           0 :             aclcheck_error(aclresult, ACL_KIND_DATABASE,
     395           0 :                            get_database_name(MyDatabaseId));
     396             : 
     397           2 :         memset(repl_null, false, sizeof(repl_null));
     398           2 :         memset(repl_repl, false, sizeof(repl_repl));
     399             : 
     400           2 :         repl_repl[Anum_pg_namespace_nspowner - 1] = true;
     401           2 :         repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
     402             : 
     403             :         /*
     404             :          * Determine the modified ACL for the new owner.  This is only
     405             :          * necessary when the ACL is non-null.
     406             :          */
     407           2 :         aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
     408             :                                    Anum_pg_namespace_nspacl,
     409             :                                    &isNull);
     410           2 :         if (!isNull)
     411             :         {
     412           0 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
     413             :                                  nspForm->nspowner, newOwnerId);
     414           0 :             repl_repl[Anum_pg_namespace_nspacl - 1] = true;
     415           0 :             repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
     416             :         }
     417             : 
     418           2 :         newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
     419             : 
     420           2 :         CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
     421             : 
     422           2 :         heap_freetuple(newtuple);
     423             : 
     424             :         /* Update owner dependency reference */
     425           2 :         changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
     426             :                                 newOwnerId);
     427             :     }
     428             : 
     429           2 :     InvokeObjectPostAlterHook(NamespaceRelationId,
     430             :                               HeapTupleGetOid(tup), 0);
     431           2 : }

Generated by: LCOV version 1.11