On Mon, Dec 27, 2010 at 2:06 PM, Robert Haas <[email protected]> wrote:
> The problem is that alter table actions AT_AddIndex and
> AT_AddConstraint don't tie neatly back to a particular piece of
> syntax. The message as written isn't incomprehensible (especially if
> you're reading it in English) but it definitely leaves something to be
> desired.
>
> Ideas?
Here's a somewhat more complete patch implementing this concept, plus
adding additional messages for non-support of constraints, rules, and
triggers. More could be done in this vein, but this picks a decent
fraction of the low-hanging fruit.
I've had to change some of the heap_open(rv) calls to
relation_open(rv) to avoid having the former throw the wrong error
message before the latter kicks in. I think there might be stylistic
objections to that, but I'm not sure what else to propose. I'm
actually pretty suspicious that many of the heap_open(rv) calls I
*didn't* change are either already a little iffy or likely to become
so once the SQL/MED stuff for foreign tables goes in. They make it
easy to forget that we've got a whole pile of relkinds and you
actually need to really think about which ones you can handle.
For example, on unpatched head:
rhaas=# create view v as select 1 as a;
CREATE VIEW
rhaas=# cluster v;
ERROR: there is no previously clustered index for table "v"
The error message is demonstrably correct in the sense that, first,
there isn't any table v, only a view v, so surely table v has no
clustered index - or anything else; and second, even if we construe
table "v" to mean view "v", it is certainly right to say it has no
clustered index because it does not - and can not - have any indexes
at all. But as undeniably true as that error message is, it's a bad
error message. With the patch:
rhaas=# cluster v;
ERROR: views do not support CLUSTER
That's more like it.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 249067f..1555b61 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -113,7 +113,9 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
Relation rel;
/* Find and lock the table */
- rel = heap_openrv(stmt->relation, AccessExclusiveLock);
+ rel = relation_openrv(stmt->relation, AccessExclusiveLock);
+ if (rel->rd_rel->relkind != RELKIND_RELATION)
+ ErrorWrongRelkind(rel, WRONG_RELKIND_FOR_COMMAND, "CLUSTER");
tableOid = RelationGetRelid(rel);
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index b578818..66df9f8 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -22,6 +22,7 @@
#include "catalog/pg_shdescription.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
+#include "commands/tablecmds.h"
#include "libpq/be-fsstubs.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
@@ -583,10 +584,8 @@ CheckAttributeComment(Relation relation)
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_VIEW &&
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or composite type",
- RelationGetRelationName(relation))));
+ ErrorWrongRelkind(relation, WRONG_RELKIND_FOR_COMMAND,
+ "COMMENT ON COLUMN");
}
/*
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 7b8bee8..488cc80 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -27,6 +27,7 @@
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
+#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "libpq/libpq.h"
@@ -998,8 +999,19 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
cstate->queryDesc = NULL;
/* Open and lock the relation, using the appropriate lock type. */
- cstate->rel = heap_openrv(stmt->relation,
+ cstate->rel = relation_openrv(stmt->relation,
(is_from ? RowExclusiveLock : AccessShareLock));
+ if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
+ {
+ if (!is_from && cstate->rel->rd_rel->relkind == RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("views do not support COPY TO"),
+ errhint("Try the COPY (SELECT ...) TO variant.")));
+ else
+ ErrorWrongRelkind(cstate->rel, WRONG_RELKIND_FOR_COMMAND,
+ is_from ? "COPY FROM" : "COPY TO");
+ }
tupDesc = RelationGetDescr(cstate->rel);
@@ -1225,29 +1237,6 @@ DoCopyTo(CopyState cstate)
{
bool pipe = (cstate->filename == NULL);
- if (cstate->rel)
- {
- if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
- {
- if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot copy from view \"%s\"",
- RelationGetRelationName(cstate->rel)),
- errhint("Try the COPY (SELECT ...) TO variant.")));
- else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot copy from sequence \"%s\"",
- RelationGetRelationName(cstate->rel))));
- else
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot copy from non-table relation \"%s\"",
- RelationGetRelationName(cstate->rel))));
- }
- }
-
if (pipe)
{
if (whereToSendOutput == DestRemote)
@@ -1701,25 +1690,6 @@ CopyFrom(CopyState cstate)
Assert(cstate->rel);
- if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
- {
- if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot copy to view \"%s\"",
- RelationGetRelationName(cstate->rel))));
- else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot copy to sequence \"%s\"",
- RelationGetRelationName(cstate->rel))));
- else
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot copy to non-table relation \"%s\"",
- RelationGetRelationName(cstate->rel))));
- }
-
/*----------
* Check to see if we can avoid writing WAL
*
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 762bbae..5c7bf29 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -366,10 +366,7 @@ CheckAttributeSecLabel(Relation relation)
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_VIEW &&
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or composite type",
- RelationGetRelationName(relation))));
+ ErrorWrongRelkind(relation, "SECURITY LABEL ON COLUMN");
}
void
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6729d83..80017e8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -222,6 +222,65 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
{'\0', 0, NULL, NULL, NULL, NULL}
};
+/*
+ * Error-reporting support for wrong-object type errors
+ */
+struct wrongtypestrings
+{
+ char kind;
+ const char *command_message;
+ const char *constraint_message;
+ const char *trigger_message;
+ const char *rule_message;
+};
+
+static const struct wrongtypestrings wrongtypestringarray[] = {
+ {RELKIND_RELATION,
+ /* translator: %s is an SQL command */
+ gettext_noop("tables do not support %s"),
+ NULL, /* constraints are supported, so no message */
+ NULL, /* rules are supported, so no message */
+ NULL}, /* triggers are supported, so no message */
+ {RELKIND_SEQUENCE,
+ /* translator: %s is an SQL command */
+ gettext_noop("sequences do not support %s"),
+ gettext_noop("sequences do not support constraints"),
+ gettext_noop("sequences do not support rules"),
+ gettext_noop("sequences do not support triggers")},
+ {RELKIND_VIEW,
+ /* translator: %s is an SQL command */
+ gettext_noop("views do not support %s"),
+ gettext_noop("views do not support constraints"),
+ NULL, /* rules are supported, so no message */
+ NULL}, /* triggers are supported, so no message */
+ {RELKIND_INDEX,
+ /* translator: %s is an SQL command */
+ gettext_noop("indexes do not support %s"),
+ gettext_noop("indexes do not support constraints"),
+ gettext_noop("indexes do not support rules"),
+ gettext_noop("indexes do not support triggers")},
+ {RELKIND_COMPOSITE_TYPE,
+ /* translator: %s is an SQL command */
+ gettext_noop("composite types do not support %s"),
+ gettext_noop("composite types do not support constraints"),
+ gettext_noop("composite types do not support rules"),
+ gettext_noop("composite types do not support triggers")},
+ {RELKIND_TOASTVALUE,
+ /* translator: %s is an SQL command */
+ gettext_noop("TOAST tables do not support %s"),
+ gettext_noop("TOAST tables do not support constraints"),
+ gettext_noop("TOAST tables do not support rules"),
+ gettext_noop("TOAST tables do not support triggers")},
+ {'\0', NULL, NULL, NULL}
+};
+
+/* Convenience strings for ATSimplePermissions */
+const char rk_relation[2] = { RELKIND_RELATION, '\0' };
+const char rk_view[2] = { RELKIND_VIEW, '\0' };
+const char rk_relation_view[3] = { RELKIND_RELATION, RELKIND_VIEW, '\0' };
+const char rk_relation_index[3] = { RELKIND_RELATION, RELKIND_INDEX, '\0' };
+const char rk_relation_type[3] =
+ { RELKIND_RELATION, RELKIND_COMPOSITE_TYPE, '\0' };
static void truncate_check_rel(Relation rel);
static List *MergeAttributes(List *schema, List *supers, char relpersistence,
@@ -264,8 +323,8 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
-static void ATSimplePermissions(Relation rel, bool allowView, bool allowType);
-static void ATSimplePermissionsRelationOrIndex(Relation rel);
+static void ATSimplePermissions(Relation rel, const char *relkinds,
+ WrongRelkindDetail detail, const char *command);
static void ATSimpleRecursion(List **wqueue, Relation rel,
AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
static void ATOneLevelRecursion(List **wqueue, Relation rel,
@@ -841,7 +900,7 @@ ExecuteTruncate(TruncateStmt *stmt)
bool recurse = interpretInhOption(rv->inhOpt);
Oid myrelid;
- rel = heap_openrv(rv, AccessExclusiveLock);
+ rel = relation_openrv(rv, AccessExclusiveLock);
myrelid = RelationGetRelid(rel);
/* don't throw error for "TRUNCATE foo, foo" */
if (list_member_oid(relids, myrelid))
@@ -868,7 +927,7 @@ ExecuteTruncate(TruncateStmt *stmt)
continue;
/* find_all_inheritors already got lock */
- rel = heap_open(childrelid, NoLock);
+ rel = relation_open(childrelid, NoLock);
truncate_check_rel(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, childrelid);
@@ -899,7 +958,7 @@ ExecuteTruncate(TruncateStmt *stmt)
Oid relid = lfirst_oid(cell);
Relation rel;
- rel = heap_open(relid, AccessExclusiveLock);
+ rel = relation_open(relid, AccessExclusiveLock);
ereport(NOTICE,
(errmsg("truncate cascades to table \"%s\"",
RelationGetRelationName(rel))));
@@ -1096,10 +1155,7 @@ truncate_check_rel(Relation rel)
/* Only allow truncate on regular tables */
if (rel->rd_rel->relkind != RELKIND_RELATION)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table",
- RelationGetRelationName(rel))));
+ ErrorWrongRelkind(rel, WRONG_RELKIND_FOR_COMMAND, "TRUNCATE");
/* Permissions checks */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
@@ -1994,8 +2050,7 @@ renameatt_internal(Oid myrelid,
relkind != RELKIND_INDEX)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, composite type or index",
- RelationGetRelationName(targetrelation))));
+ errmsg("system-generated column names may not be altered")));
/*
* permissions checking. only the owner of a class can change its schema.
@@ -2684,14 +2739,17 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
switch (cmd->subtype)
{
case AT_AddColumn: /* ADD COLUMN */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, rk_relation_type,
+ WRONG_RELKIND_FOR_COMMAND, "ADD COLUMN");
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */
- ATSimplePermissions(rel, true, false);
+ ATSimplePermissions(rel, rk_view,
+ WRONG_RELKIND_FOR_COMMAND,
+ "ADD COLUMN");
/* Performs own recursion */
ATPrepAddColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ADD_COL;
@@ -2704,19 +2762,25 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
* substitutes default values into INSERTs before it expands
* rules.
*/
- ATSimplePermissions(rel, true, false);
+ ATSimplePermissions(rel, rk_relation_view,
+ WRONG_RELKIND_FOR_COMMAND,
+ "ALTER COLUMN DEFAULT");
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "DROP NOT NULL");
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_DROP;
break;
case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "SET NOT NULL");
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_ADD_CONSTR;
@@ -2728,31 +2792,47 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_MISC;
break;
case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
+ /* This command never recurses */
+ ATSimplePermissions(rel, rk_relation_index,
+ WRONG_RELKIND_FOR_COMMAND,
+ "ALTER COLUMN SET (...)");
+ pass = AT_PASS_MISC;
+ break;
case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
- ATSimplePermissionsRelationOrIndex(rel);
/* This command never recurses */
+ ATSimplePermissions(rel, rk_relation_index,
+ WRONG_RELKIND_FOR_COMMAND,
+ "ALTER COLUMN RESET (...)");
pass = AT_PASS_MISC;
break;
case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "ALTER COLUMN SET STORAGE");
ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode);
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_DropColumn: /* DROP COLUMN */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, rk_relation_type,
+ WRONG_RELKIND_FOR_COMMAND,
+ "DROP COLUMN");
ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode);
/* Recursion occurs during execution phase */
pass = AT_PASS_DROP;
break;
case AT_AddIndex: /* ADD INDEX */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_CONSTRAINTS,
+ NULL);
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_ADD_INDEX;
break;
case AT_AddConstraint: /* ADD CONSTRAINT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_CONSTRAINTS,
+ NULL);
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
@@ -2760,7 +2840,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_ADD_CONSTR;
break;
case AT_DropConstraint: /* DROP CONSTRAINT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_CONSTRAINTS,
+ NULL);
/* Recursion occurs during execution phase */
/* No command-specific prep needed except saving recurse flag */
if (recurse)
@@ -2768,7 +2850,9 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_DROP;
break;
case AT_AlterColumnType: /* ALTER COLUMN TYPE */
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, rk_relation_type,
+ WRONG_RELKIND_FOR_COMMAND,
+ "ALTER COLUMN TYPE");
/* Performs own recursion */
ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd, lockmode);
pass = AT_PASS_ALTER_TYPE;
@@ -2779,21 +2863,34 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_MISC;
break;
case AT_ClusterOn: /* CLUSTER ON */
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "CLUSTER ON");
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
case AT_DropCluster: /* SET WITHOUT CLUSTER */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "SET WITHOUT CLUSTER");
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_AddOids: /* SET WITH OIDS */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "SET WITH OIDS");
/* Performs own recursion */
if (!rel->rd_rel->relhasoids || recursing)
ATPrepAddOids(wqueue, rel, recurse, cmd, lockmode);
pass = AT_PASS_ADD_COL;
break;
case AT_DropOids: /* SET WITHOUT OIDS */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "SET WITHOUT OIDS");
/* Performs own recursion */
if (rel->rd_rel->relhasoids)
{
@@ -2807,20 +2904,32 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
pass = AT_PASS_DROP;
break;
case AT_SetTableSpace: /* SET TABLESPACE */
- ATSimplePermissionsRelationOrIndex(rel);
+ ATSimplePermissions(rel, rk_relation_index,
+ WRONG_RELKIND_FOR_COMMAND,
+ "SET TABLESPACE");
/* This command never recurses */
ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
+ ATSimplePermissions(rel, rk_relation_index,
+ WRONG_RELKIND_FOR_COMMAND,
+ "SET (...)");
+ /* This command never recurses */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
case AT_ResetRelOptions: /* RESET (...) */
- ATSimplePermissionsRelationOrIndex(rel);
+ ATSimplePermissions(rel, rk_relation_index,
+ WRONG_RELKIND_FOR_COMMAND,
+ "RESET (...)");
/* This command never recurses */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
case AT_AddInherit: /* INHERIT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "INHERIT");
/* This command never recurses */
ATPrepAddInherit(rel);
pass = AT_PASS_MISC;
@@ -2833,12 +2942,28 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_DisableTrig: /* DISABLE TRIGGER variants */
case AT_DisableTrigAll:
case AT_DisableTrigUser:
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_TRIGGERS,
+ NULL);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
case AT_EnableAlwaysRule:
case AT_EnableReplicaRule:
case AT_DisableRule:
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_RULES,
+ NULL);
+ /* These commands never recurse */
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
case AT_DropInherit: /* NO INHERIT */
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "NO INHERIT");
/* These commands never recurse */
/* No command-specific prep needed */
pass = AT_PASS_MISC;
@@ -3559,66 +3684,16 @@ ATGetQueueEntry(List **wqueue, Relation rel)
/*
* ATSimplePermissions
*
- * - Ensure that it is a relation (or possibly a view)
- * - Ensure this user is the owner
- * - Ensure that it is not a system table
- */
-static void
-ATSimplePermissions(Relation rel, bool allowView, bool allowType)
-{
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- {
- if (allowView)
- {
- if (rel->rd_rel->relkind != RELKIND_VIEW)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or view",
- RelationGetRelationName(rel))));
- }
- else if (allowType)
- {
- if (rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or composite type",
- RelationGetRelationName(rel))));
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table",
- RelationGetRelationName(rel))));
- }
-
- /* Permissions checks */
- if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(rel));
-
- if (!allowSystemTableMods && IsSystemRelation(rel))
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("permission denied: \"%s\" is a system catalog",
- RelationGetRelationName(rel))));
-}
-
-/*
- * ATSimplePermissionsRelationOrIndex
- *
- * - Ensure that it is a relation or an index
+ * - Check the relkind against the provided list
* - Ensure this user is the owner
* - Ensure that it is not a system table
*/
static void
-ATSimplePermissionsRelationOrIndex(Relation rel)
+ATSimplePermissions(Relation rel, const char *relkinds,
+ WrongRelkindDetail detail, const char *command)
{
- if (rel->rd_rel->relkind != RELKIND_RELATION &&
- rel->rd_rel->relkind != RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or index",
- RelationGetRelationName(rel))));
+ if (!strchr(relkinds, rel->rd_rel->relkind))
+ ErrorWrongRelkind(rel, detail, command);
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
@@ -4449,10 +4524,8 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
*/
if (rel->rd_rel->relkind != RELKIND_RELATION &&
rel->rd_rel->relkind != RELKIND_INDEX)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or index",
- RelationGetRelationName(rel))));
+ ErrorWrongRelkind(rel, WRONG_RELKIND_FOR_COMMAND,
+ "ALTER COLUMN .. SET STATISTICS");
/* Permissions checks */
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
@@ -4692,7 +4765,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, true);
+ ATSimplePermissions(rel, rk_relation_type,
+ WRONG_RELKIND_FOR_COMMAND,
+ "DROP COLUMN");
/*
* get the number of the attribute
@@ -4996,7 +5071,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_CONSTRAINTS,
+ NULL);
/*
* Call AddRelationNewConstraints to do the work, making sure it works on
@@ -5940,7 +6017,9 @@ ATExecDropConstraint(Relation rel, const char *constrName,
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
- ATSimplePermissions(rel, false, false);
+ ATSimplePermissions(rel, rk_relation,
+ WRONG_RELKIND_FOR_CONSTRAINTS,
+ NULL);
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
@@ -6881,10 +6960,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
break;
/* FALL THRU */
default:
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or sequence",
- NameStr(tuple_class->relname))));
+ ErrorWrongRelkind(target_rel, WRONG_RELKIND_FOR_COMMAND,
+ "OWNER TO");
}
/*
@@ -7188,10 +7265,8 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode
(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
break;
default:
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, index, or TOAST table",
- RelationGetRelationName(rel))));
+ ErrorWrongRelkind(rel, WRONG_RELKIND_FOR_COMMAND, isReset ?
+ "RESET (...)" : "SET (...)");
break;
}
@@ -7543,7 +7618,9 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
* Must be owner of both parent and child -- child was checked by
* ATSimplePermissions call in ATPrepCmd
*/
- ATSimplePermissions(parent_rel, false, false);
+ ATSimplePermissions(parent_rel, rk_relation,
+ WRONG_RELKIND_FOR_COMMAND,
+ "INHERIT");
/* Permanent rels cannot inherit from temporary ones */
if (RelationUsesTempNamespace(parent_rel)
@@ -8186,10 +8263,8 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
case RELKIND_TOASTVALUE:
/* FALL THRU */
default:
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, or sequence",
- RelationGetRelationName(rel))));
+ ErrorWrongRelkind(rel, WRONG_RELKIND_FOR_COMMAND,
+ "SET SCHEMA");
}
/* get schema OID and check its permissions */
@@ -8376,6 +8451,50 @@ AlterSeqNamespaces(Relation classRel, Relation rel,
relation_close(depRel, AccessShareLock);
}
+/*
+ * Emit the right error message for a SQL command issue on a relation of
+ * the wrong type.
+ */
+void
+ErrorWrongRelkind(Relation rel, WrongRelkindDetail detail,
+ const char *command)
+{
+ const struct wrongtypestrings *entry;
+
+ for (entry = wrongtypestringarray; entry->kind != '\0'; entry++)
+ if (entry->kind == rel->rd_rel->relkind)
+ break;
+ if (entry->kind == '\0')
+ elog(ERROR, "unexpected relkind: %c", rel->rd_rel->relkind);
+ switch (detail)
+ {
+ case WRONG_RELKIND_FOR_COMMAND:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg(entry->command_message, command)));
+ break;
+ case WRONG_RELKIND_FOR_CONSTRAINTS:
+ Assert(entry->constraint_message != NULL);
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s", _(entry->constraint_message))));
+ break;
+ case WRONG_RELKIND_FOR_TRIGGERS:
+ Assert(entry->trigger_message != NULL);
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s", _(entry->trigger_message))));
+ break;
+ case WRONG_RELKIND_FOR_RULES:
+ Assert(entry->rule_message != NULL);
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s", _(entry->rule_message))));
+ break;
+ default:
+ elog(ERROR, "unexpected WrongRelkindDetail");
+ }
+}
/*
* This code supports
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 8195392..3c2d352 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -27,6 +27,7 @@
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/instrument.h"
@@ -149,7 +150,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
* need to take an AccessExclusiveLock to add one of those, just as we do
* with ON SELECT rules.
*/
- rel = heap_openrv(stmt->relation, ShareRowExclusiveLock);
+ rel = relation_openrv(stmt->relation, ShareRowExclusiveLock);
/*
* Triggers must be on tables or views, and there are additional
@@ -187,10 +188,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
errdetail("Views cannot have TRUNCATE triggers.")));
}
else
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or view",
- RelationGetRelationName(rel))));
+ ErrorWrongRelkind(rel, WRONG_RELKIND_FOR_TRIGGERS, NULL);
if (!allowSystemTableMods && IsSystemRelation(rel))
ereport(ERROR,
@@ -1089,10 +1087,7 @@ RemoveTriggerById(Oid trigOid)
if (rel->rd_rel->relkind != RELKIND_RELATION &&
rel->rd_rel->relkind != RELKIND_VIEW)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or view",
- RelationGetRelationName(rel))));
+ ErrorWrongRelkind(rel, WRONG_RELKIND_FOR_TRIGGERS, NULL);
if (!allowSystemTableMods && IsSystemRelation(rel))
ereport(ERROR,
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index aa7c144..90bf17e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1406,7 +1406,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
int count;
Assert(IsA(inh, RangeVar));
- rel = heap_openrv(inh, AccessShareLock);
+ rel = relation_openrv(inh, AccessShareLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 4354897..d81bce6 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -22,6 +22,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_rewrite.h"
#include "catalog/storage.h"
+#include "commands/tablecmds.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_utilcmd.h"
@@ -255,10 +256,7 @@ DefineQueryRewrite(char *rulename,
*/
if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
event_relation->rd_rel->relkind != RELKIND_VIEW)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table or view",
- RelationGetRelationName(event_relation))));
+ ErrorWrongRelkind(event_relation, WRONG_RELKIND_FOR_RULES, NULL);
if (!allowSystemTableMods && IsSystemRelation(event_relation))
ereport(ERROR,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index ad932d3..9a16488 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -60,6 +60,16 @@ extern AttrNumber *varattnos_map(TupleDesc olddesc, TupleDesc newdesc);
extern AttrNumber *varattnos_map_schema(TupleDesc old, List *schema);
extern void change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
+typedef enum
+{
+ WRONG_RELKIND_FOR_COMMAND,
+ WRONG_RELKIND_FOR_CONSTRAINTS,
+ WRONG_RELKIND_FOR_TRIGGERS,
+ WRONG_RELKIND_FOR_RULES
+} WrongRelkindDetail;
+extern void ErrorWrongRelkind(Relation rel, WrongRelkindDetail detail,
+ const char *command);
+
extern void register_on_commit_action(Oid relid, OnCommitAction action);
extern void remove_on_commit_action(Oid relid);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index e415730..b7917ea 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -599,9 +599,9 @@ ERROR: cannot alter system column "oid"
-- try creating a view and altering that, should fail
create view myview as select * from atacc1;
alter table myview alter column test drop not null;
-ERROR: "myview" is not a table
+ERROR: views do not support DROP NOT NULL
alter table myview alter column test set not null;
-ERROR: "myview" is not a table
+ERROR: views do not support SET NOT NULL
drop view myview;
drop table atacc1;
-- test inheritance
@@ -854,7 +854,7 @@ select * from myview;
(0 rows)
alter table myview drop d;
-ERROR: "myview" is not a table or composite type
+ERROR: views do not support DROP COLUMN
drop view myview;
-- test some commands to make sure they fail on the dropped column
analyze atacc1(a);
diff --git a/src/test/regress/expected/copyselect.out b/src/test/regress/expected/copyselect.out
index cbc140c..d7a04a0 100644
--- a/src/test/regress/expected/copyselect.out
+++ b/src/test/regress/expected/copyselect.out
@@ -30,7 +30,7 @@ copy test1 to stdout;
-- This should fail
--
copy v_test1 to stdout;
-ERROR: cannot copy from view "v_test1"
+ERROR: views do not support COPY TO
HINT: Try the COPY (SELECT ...) TO variant.
--
-- Test COPY (select) TO
@@ -109,9 +109,9 @@ t
-- This should fail
--
\copy v_test1 to stdout
-ERROR: cannot copy from view "v_test1"
+ERROR: views do not support COPY TO
HINT: Try the COPY (SELECT ...) TO variant.
-\copy: ERROR: cannot copy from view "v_test1"
+\copy: ERROR: views do not support COPY TO
HINT: Try the COPY (SELECT ...) TO variant.
--
-- Test \copy (select ...)
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers