On Mon, Dec 19, 2011 at 11:52:54PM -0500, Robert Haas wrote:
> After staring at this for quite a while longer, it seemed to me that
> the logic for renaming a relation was similar enough to the logic for
> changing a schema that the two calbacks could reasonably be combined
> using a bit of conditional logic; and that, further, the same callback
> could be used, with a small amount of additional modification, for
> ALTER TABLE.  Here's a patch to do that.

Nice.

> I also notice that cluster() - which doesn't have a callback - has
> exactly the same needs as ReindexRelation() - which does.  So that
> case can certainly share code; though I'm not quite sure what to call
> the shared callback, or which file to put it in.
> RangeVarCallbackForStorageRewrite?

I'd put it in tablecmds.c and name it RangeVarCallbackOwnsTable.


A few things on the patch:

> --- a/src/backend/commands/tablecmds.c
> +++ b/src/backend/commands/tablecmds.c

> @@ -2560,90 +2500,26 @@ CheckTableNotInUse(Relation rel, const char *stmt)
>   * Thanks to the magic of MVCC, an error anywhere along the way rolls back
>   * the whole operation; we don't have to do anything special to clean up.
>   *
> - * We lock the table as the first action, with an appropriate lock level
> + * The caller must lock the relation, with an appropriate lock level 
>   * for the subcommands requested. Any subcommand that needs to rewrite
>   * tuples in the table forces the whole command to be executed with
> - * AccessExclusiveLock. If all subcommands do not require rewrite table
> - * then we may be able to use lower lock levels. We pass the lock level down
> + * AccessExclusiveLock (actually, that is currently required always, but
> + * we hope to relax it at some point).  We pass the lock level down
>   * so that we can apply it recursively to inherited tables. Note that the
> - * lock level we want as we recurse may well be higher than required for
> + * lock level we want as we recurse might well be higher than required for
>   * that specific subcommand. So we pass down the overall lock requirement,
>   * rather than reassess it at lower levels.
>   */
>  void
> -AlterTable(AlterTableStmt *stmt)
> +AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt)
>  {
>       Relation        rel;
> -     LOCKMODE        lockmode = AlterTableGetLockLevel(stmt->cmds);
>  
> -     /*
> -      * Acquire same level of lock as already acquired during parsing.
> -      */
> -     rel = relation_openrv(stmt->relation, lockmode);
> +     /* Caller is required to provide an adequate lock. */
> +     rel = relation_open(relid, NoLock);
>  
>       CheckTableNotInUse(rel, "ALTER TABLE");
>  
> -     /* Check relation type against type specified in the ALTER command */
> -     switch (stmt->relkind)
> -     {
> -             case OBJECT_TABLE:
> -
> -                     /*
> -                      * For mostly-historical reasons, we allow ALTER TABLE 
> to apply to
> -                      * almost all relation types.
> -                      */
> -                     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE
> -                             || rel->rd_rel->relkind == 
> RELKIND_FOREIGN_TABLE)
> -                             ereport(ERROR,
> -                                             
> (errcode(ERRCODE_WRONG_OBJECT_TYPE),
> -                                              errmsg("\"%s\" is not a table",
> -                                                             
> RelationGetRelationName(rel))));

RangeVarCallbackForAlterRelation() does not preserve ALTER TABLE's refusal to
operate on foreign tables.

> -                     break;
> -
> -             case OBJECT_INDEX:
> -                     if (rel->rd_rel->relkind != RELKIND_INDEX)
> -                             ereport(ERROR,
> -                                             
> (errcode(ERRCODE_WRONG_OBJECT_TYPE),
> -                                              errmsg("\"%s\" is not an 
> index",
> -                                                             
> RelationGetRelationName(rel))));
> -                     break;
> -
> -             case OBJECT_SEQUENCE:
> -                     if (rel->rd_rel->relkind != RELKIND_SEQUENCE)
> -                             ereport(ERROR,
> -                                             
> (errcode(ERRCODE_WRONG_OBJECT_TYPE),
> -                                              errmsg("\"%s\" is not a 
> sequence",
> -                                                             
> RelationGetRelationName(rel))));
> -                     break;
> -
> -             case OBJECT_TYPE:
> -                     if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
> -                             ereport(ERROR,
> -                                             
> (errcode(ERRCODE_WRONG_OBJECT_TYPE),
> -                                              errmsg("\"%s\" is not a 
> composite type",
> -                                                             
> RelationGetRelationName(rel))));
> -                     break;
> -
> -             case OBJECT_VIEW:
> -                     if (rel->rd_rel->relkind != RELKIND_VIEW)
> -                             ereport(ERROR,
> -                                             
> (errcode(ERRCODE_WRONG_OBJECT_TYPE),
> -                                              errmsg("\"%s\" is not a view",
> -                                                             
> RelationGetRelationName(rel))));
> -                     break;
> -
> -             case OBJECT_FOREIGN_TABLE:
> -                     if (rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
> -                             ereport(ERROR,
> -                                             
> (errcode(ERRCODE_WRONG_OBJECT_TYPE),
> -                                              errmsg("\"%s\" is not a 
> foreign table",
> -                                                             
> RelationGetRelationName(rel))));
> -                     break;
> -
> -             default:
> -                     elog(ERROR, "unrecognized object type: %d", (int) 
> stmt->relkind);

RangeVarCallbackForAlterRelation() does not preserve the check for unexpected
object types.

> -     }
> -
>       ATController(rel, stmt->cmds, 
> interpretInhOption(stmt->relation->inhOpt),
>                                lockmode);
>  }

> --- a/src/backend/tcop/utility.c
> +++ b/src/backend/tcop/utility.c
> @@ -699,12 +699,23 @@ standard_ProcessUtility(Node *parsetree,
>  
>               case T_AlterTableStmt:
>                       {
> +                             AlterTableStmt *atstmt = (AlterTableStmt *) 
> parsetree;
> +                             Oid                     relid;
>                               List       *stmts;
>                               ListCell   *l;
> +                             LOCKMODE        lockmode;
> +
> +                             /*
> +                              * Figure out lock mode, and acquire lock.  
> This also does
> +                              * basic permissions checks, so that we won't 
> wait for a lock
> +                              * on (for example) a relation on which we have 
> no
> +                              * permissions.
> +                              */
> +                             lockmode = AlterTableGetLockLevel(atstmt->cmds);
> +                             relid = AlterTableLookupRelation(atstmt, 
> lockmode);
>  
>                               /* Run parse analysis ... */
> -                             stmts = transformAlterTableStmt((AlterTableStmt 
> *) parsetree,
> -                                                                             
>                 queryString);
> +                             stmts = transformAlterTableStmt(atstmt, 
> queryString);

utility.c doesn't take locks for any other command; parse analysis usually
does that.  To preserve that modularity, you could add a "bool toplevel"
argument to transformAlterTableStmt().  Pass true here, false in
ATPostAlterTypeParse().  If true, use AlterTableLookupRelation() to get full
security checks.  Otherwise, just call relation_openrv() as now.  Would that
be an improvement?

>  
>                               /* ... and do it */
>                               foreach(l, stmts)

nm

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to