Re: RangeVarGetRelid()

From: Noah Misch <noah(at)leadboat(dot)com>
To: Robert Haas <robertmhaas(at)gmail(dot)com>
Cc: Alvaro Herrera <alvherre(at)commandprompt(dot)com>, Pg Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: RangeVarGetRelid()
Date: 2011-12-21 01:14:43
Message-ID: 20111221011443.GA9507@tornado.leadboat.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: 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

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Tom Lane 2011-12-21 01:23:36 Re: sorting table columns
Previous Message Tom Lane 2011-12-21 01:03:47 Re: [PATCH] Fix ScalarArrayOpExpr estimation for GIN indexes