]> granicus.if.org Git - postgresql/commitdiff
Fix bugs in relpersistence handling during table creation.
authorRobert Haas <rhaas@postgresql.org>
Sun, 3 Jul 2011 21:34:47 +0000 (17:34 -0400)
committerRobert Haas <rhaas@postgresql.org>
Sun, 3 Jul 2011 21:46:58 +0000 (17:46 -0400)
Unlike the relistemp field which it replaced, relpersistence must be
set correctly quite early during the table creation process, as we
rely on it quite early on for a number of purposes, including security
checks.  Normally, this is set based on whether the user enters CREATE
TABLE, CREATE UNLOGGED TABLE, or CREATE TEMPORARY TABLE, but a
relation may also be made implicitly temporary by creating it in
pg_temp.  This patch fixes the handling of that case, and also
disables creation of unlogged tables in temporary tablespace (such
table indeed skip WAL-logging, but we reject an explicit
specification) and creation of relations in the temporary schemas of
other sessions (which is not very sensible, and didn't work right
anyway).

Report by Amit Khandekar.

src/backend/catalog/namespace.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/commands/view.c
src/backend/executor/execMain.c
src/backend/parser/parse_utilcmd.c
src/include/catalog/namespace.h
src/test/regress/expected/create_view.out

index 41e92992deec95301fdda6d51972f090ec5f9b72..ce795a61c5bb1ec82f1989980b8c4a0c76033fae 100644 (file)
@@ -311,19 +311,6 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation)
                                                        newRelation->relname)));
        }
 
-       if (newRelation->relpersistence == RELPERSISTENCE_TEMP)
-       {
-               /* TEMP tables are created in our backend-local temp namespace */
-               if (newRelation->schemaname)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                  errmsg("temporary tables cannot specify a schema name")));
-               /* Initialize temp namespace if first time through */
-               if (!OidIsValid(myTempNamespace))
-                       InitTempTableNamespace();
-               return myTempNamespace;
-       }
-
        if (newRelation->schemaname)
        {
                /* check for pg_temp alias */
@@ -338,6 +325,13 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation)
                namespaceId = get_namespace_oid(newRelation->schemaname, false);
                /* we do not check for USAGE rights here! */
        }
+       else if (newRelation->relpersistence == RELPERSISTENCE_TEMP)
+       {
+               /* Initialize temp namespace if first time through */
+               if (!OidIsValid(myTempNamespace))
+                       InitTempTableNamespace();
+               return myTempNamespace;
+       }
        else
        {
                /* use the default creation namespace */
@@ -389,6 +383,43 @@ RangeVarGetAndCheckCreationNamespace(const RangeVar *newRelation)
        return namespaceId;
 }
 
+/*
+ * Adjust the relpersistence for an about-to-be-created relation based on the
+ * creation namespace, and throw an error for invalid combinations.
+ */
+void
+RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid)
+{
+       switch (newRelation->relpersistence)
+       {
+               case RELPERSISTENCE_TEMP:
+                       if (!isTempOrToastNamespace(nspid))
+                       {
+                               if (isAnyTempNamespace(nspid))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                                        errmsg("cannot create relations in temporary schemas of other sessions")));
+                               else
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                                        errmsg("cannot create temporary relation in non-temporary schema")));
+                       }
+                       break;
+               case RELPERSISTENCE_PERMANENT:
+                       if (isTempOrToastNamespace(nspid))
+                               newRelation->relpersistence = RELPERSISTENCE_TEMP;
+                       else if (isAnyTempNamespace(nspid))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                                errmsg("cannot create relations in temporary schemas of other sessions")));
+                       break;
+               default:
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                        errmsg("only temporary relations may be created in temporary schemas")));
+       }
+}
+
 /*
  * RelnameGetRelid
  *             Try to resolve an unqualified relation name.
index 557d5417addbc70d61ed2c199f4e01de47db78e0..fcfe1faeb45d973a4c8577b963c1f4b3d8630931 100644 (file)
@@ -428,6 +428,13 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("constraints on foreign tables are not supported")));
 
+       /*
+        * Look up the namespace in which we are supposed to create the relation,
+        * and check we have permission to create there.
+        */
+       namespaceId = RangeVarGetAndCheckCreationNamespace(stmt->relation);
+       RangeVarAdjustRelationPersistence(stmt->relation, namespaceId);
+
        /*
         * Security check: disallow creating temp tables from security-restricted
         * code.  This is needed because calling code might not expect untrusted
@@ -439,12 +446,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("cannot create temporary table within security-restricted operation")));
 
-       /*
-        * Look up the namespace in which we are supposed to create the relation,
-        * and check we have permission to create there.
-        */
-       namespaceId = RangeVarGetAndCheckCreationNamespace(stmt->relation);
-
        /*
         * Select tablespace to use.  If not specified, use default tablespace
         * (which may in turn default to database's default).
index 66c11de6723ab95c9fe72e7655fb3df41db8727e..18e80221070c7d873aba06db31893ef81a2d3c51 100644 (file)
@@ -1609,6 +1609,7 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
         * instead of below about a "relation".
         */
        typeNamespace = RangeVarGetCreationNamespace(createStmt->relation);
+       RangeVarAdjustRelationPersistence(createStmt->relation, typeNamespace);
        old_type_oid =
                GetSysCacheOid2(TYPENAMENSP,
                                                CStringGetDatum(createStmt->relation->relname),
index be681e3fd4f9fa24c3b8fe4fc22dd13549e02f03..b23819965869a58af27ac5633cb20af1664228bc 100644 (file)
@@ -97,10 +97,10 @@ isViewOnTempTable_walker(Node *node, void *context)
  *---------------------------------------------------------------------
  */
 static Oid
-DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
+DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
+                                         Oid namespaceId)
 {
-       Oid                     viewOid,
-                               namespaceId;
+       Oid                     viewOid;
        CreateStmt *createStmt = makeNode(CreateStmt);
        List       *attrList;
        ListCell   *t;
@@ -160,7 +160,6 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
        /*
         * Check to see if we want to replace an existing view.
         */
-       namespaceId = RangeVarGetCreationNamespace(relation);
        viewOid = get_relname_relid(relation->relname, namespaceId);
 
        if (OidIsValid(viewOid) && replace)
@@ -417,6 +416,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
 {
        Query      *viewParse;
        Oid                     viewOid;
+       Oid                     namespaceId;
        RangeVar   *view;
 
        /*
@@ -480,28 +480,31 @@ DefineView(ViewStmt *stmt, const char *queryString)
                                                        "names than columns")));
        }
 
+       /* Unlogged views are not sensible. */
+       if (stmt->view->relpersistence == RELPERSISTENCE_UNLOGGED)
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+               errmsg("views cannot be unlogged because they do not have storage")));
+
        /*
         * If the user didn't explicitly ask for a temporary view, check whether
         * we need one implicitly.      We allow TEMP to be inserted automatically as
         * long as the CREATE command is consistent with that --- no explicit
         * schema name.
         */
-       view = stmt->view;
+       view = copyObject(stmt->view);  /* don't corrupt original command */
        if (view->relpersistence == RELPERSISTENCE_PERMANENT
                && isViewOnTempTable(viewParse))
        {
-               view = copyObject(view);        /* don't corrupt original command */
                view->relpersistence = RELPERSISTENCE_TEMP;
                ereport(NOTICE,
                                (errmsg("view \"%s\" will be a temporary view",
                                                view->relname)));
        }
 
-       /* Unlogged views are not sensible. */
-       if (view->relpersistence == RELPERSISTENCE_UNLOGGED)
-               ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-               errmsg("views cannot be unlogged because they do not have storage")));
+       /* Might also need to make it temporary if placed in temp schema. */
+       namespaceId = RangeVarGetCreationNamespace(view);
+       RangeVarAdjustRelationPersistence(view, namespaceId);
 
        /*
         * Create the view relation
@@ -510,7 +513,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
         * aborted.
         */
        viewOid = DefineVirtualRelation(view, viewParse->targetList,
-                                                                       stmt->replace);
+                                                                       stmt->replace, namespaceId);
 
        /*
         * The relation we have just created is not visible to any other commands
index df1d3cab4d16258017439ec4d69e12ae695cc3d1..1d92f5980b4f006d99c99f35206f5a11dafe85ce 100644 (file)
@@ -2414,6 +2414,13 @@ OpenIntoRel(QueryDesc *queryDesc)
                                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                 errmsg("ON COMMIT can only be used on temporary tables")));
 
+       /*
+        * Find namespace to create in, check its permissions
+        */
+       intoName = into->rel->relname;
+       namespaceId = RangeVarGetAndCheckCreationNamespace(into->rel);
+       RangeVarAdjustRelationPersistence(into->rel, namespaceId);
+
        /*
         * Security check: disallow creating temp tables from security-restricted
         * code.  This is needed because calling code might not expect untrusted
@@ -2425,12 +2432,6 @@ OpenIntoRel(QueryDesc *queryDesc)
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("cannot create temporary table within security-restricted operation")));
 
-       /*
-        * Find namespace to create in, check its permissions
-        */
-       intoName = into->rel->relname;
-       namespaceId = RangeVarGetAndCheckCreationNamespace(into->rel);
-
        /*
         * Select tablespace to use.  If not specified, use default tablespace
         * (which may in turn default to database's default).
index 622efe592d406e375f894d1ba0cd3b2633e4cb2c..d53286cd2a0e3f5b3d026f7a69b2fbf8b5f86a20 100644 (file)
@@ -162,6 +162,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
         * possible.
         */
        namespaceid = RangeVarGetAndCheckCreationNamespace(stmt->relation);
+       RangeVarAdjustRelationPersistence(stmt->relation, namespaceid);
 
        /*
         * If the relation already exists and the user specified "IF NOT EXISTS",
@@ -367,7 +368,10 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
                if (cxt->rel)
                        snamespaceid = RelationGetNamespace(cxt->rel);
                else
+               {
                        snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
+                       RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
+               }
                snamespace = get_namespace_name(snamespaceid);
                sname = ChooseRelationName(cxt->relation->relname,
                                                                   column->colname,
index 53600969ad7e29ef5875ff3c1feb3edd59e2c822..7e1e194794c2055912a6dcff27777e49235c4158 100644 (file)
@@ -50,6 +50,7 @@ typedef struct OverrideSearchPath
 extern Oid     RangeVarGetRelid(const RangeVar *relation, bool failOK);
 extern Oid     RangeVarGetCreationNamespace(const RangeVar *newRelation);
 extern Oid     RangeVarGetAndCheckCreationNamespace(const RangeVar *newRelation);
+extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
 extern Oid     RelnameGetRelid(const char *relname);
 extern bool RelationIsVisible(Oid relid);
 
index f2c06854d0f8ce54e40737f838b5fc06d79ee226..f9490a3a5506268a8e462f165a2c0530d725c4c1 100644 (file)
@@ -81,11 +81,11 @@ CREATE VIEW temp_view_test.v2 AS SELECT * FROM base_table;
 -- should fail
 CREATE VIEW temp_view_test.v3_temp AS SELECT * FROM temp_table;
 NOTICE:  view "v3_temp" will be a temporary view
-ERROR:  temporary tables cannot specify a schema name
+ERROR:  cannot create temporary relation in non-temporary schema
 -- should fail
 CREATE SCHEMA test_schema
     CREATE TEMP VIEW testview AS SELECT 1;
-ERROR:  temporary tables cannot specify a schema name
+ERROR:  cannot create temporary relation in non-temporary schema
 -- joins: if any of the join relations are temporary, the view
 -- should also be temporary
 -- should be non-temp