Re: RangeVarGetRelid() - Mailing list pgsql-hackers

From Noah Misch
Subject Re: RangeVarGetRelid()
Date
Msg-id 20111221011443.GA9507@tornado.leadboat.com
Whole thread Raw
In response to Re: RangeVarGetRelid()  (Robert Haas <robertmhaas@gmail.com>)
Responses Re: RangeVarGetRelid()
List pgsql-hackers
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


pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: [PATCH] Fix ScalarArrayOpExpr estimation for GIN indexes
Next
From: Tom Lane
Date:
Subject: Re: sorting table columns