diff --git a/contrib/amcheck/expected/check_heap.out b/contrib/amcheck/expected/check_heap.out index 1fb3823142..ad3086d2aa 100644 --- a/contrib/amcheck/expected/check_heap.out +++ b/contrib/amcheck/expected/check_heap.out @@ -139,7 +139,8 @@ CREATE TABLE test_partitioned (a int, b text default repeat('x', 5000)) SELECT * FROM verify_heapam('test_partitioned', startblock := NULL, endblock := NULL); -ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +ERROR: cannot check relation "test_partitioned" +DETAIL: This operation is not supported for partitioned tables. -- Check that valid options are not rejected nor corruption reported -- for an empty partition table (the child one) CREATE TABLE test_partition partition OF test_partitioned FOR VALUES IN (1); @@ -165,19 +166,22 @@ CREATE INDEX test_index ON test_partition (a); SELECT * FROM verify_heapam('test_index', startblock := NULL, endblock := NULL); -ERROR: "test_index" is not a table, materialized view, or TOAST table +ERROR: cannot check relation "test_index" +DETAIL: This operation is not supported for indexes. -- Check that views are rejected CREATE VIEW test_view AS SELECT 1; SELECT * FROM verify_heapam('test_view', startblock := NULL, endblock := NULL); -ERROR: "test_view" is not a table, materialized view, or TOAST table +ERROR: cannot check relation "test_view" +DETAIL: This operation is not supported for views. -- Check that sequences are rejected CREATE SEQUENCE test_sequence; SELECT * FROM verify_heapam('test_sequence', startblock := NULL, endblock := NULL); -ERROR: "test_sequence" is not a table, materialized view, or TOAST table +ERROR: cannot check relation "test_sequence" +DETAIL: This operation is not supported for sequences. -- Check that foreign tables are rejected CREATE FOREIGN DATA WRAPPER dummy; CREATE SERVER dummy_server FOREIGN DATA WRAPPER dummy; @@ -185,7 +189,8 @@ CREATE FOREIGN TABLE test_foreign_table () SERVER dummy_server; SELECT * FROM verify_heapam('test_foreign_table', startblock := NULL, endblock := NULL); -ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +ERROR: cannot check relation "test_foreign_table" +DETAIL: This operation is not supported for foreign tables. -- cleanup DROP TABLE heaptest; DROP TABLE test_partition; diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c index a3caee7cdd..226271923a 100644 --- a/contrib/amcheck/verify_heapam.c +++ b/contrib/amcheck/verify_heapam.c @@ -147,7 +147,6 @@ typedef struct HeapCheckContext } HeapCheckContext; /* Internal implementation */ -static void sanity_check_relation(Relation rel); static void check_tuple(HeapCheckContext *ctx); static void check_toast_tuple(HeapTuple toasttup, HeapCheckContext *ctx, ToastedAttribute *ta, int32 *expected_chunk_seq, @@ -300,7 +299,23 @@ verify_heapam(PG_FUNCTION_ARGS) /* Open relation, check relkind and access method */ ctx.rel = relation_open(relid, AccessShareLock); - sanity_check_relation(ctx.rel); + + /* + * Check that a relation's relkind and access method are both supported. + */ + if (ctx.rel->rd_rel->relkind != RELKIND_RELATION && + ctx.rel->rd_rel->relkind != RELKIND_MATVIEW && + ctx.rel->rd_rel->relkind != RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot check relation \"%s\"", + RelationGetRelationName(ctx.rel)), + errdetail_relkind_not_supported(ctx.rel->rd_rel->relkind))); + + if (ctx.rel->rd_rel->relam != HEAP_TABLE_AM_OID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only heap AM is supported"))); /* Early exit if the relation is empty */ nblocks = RelationGetNumberOfBlocks(ctx.rel); @@ -523,25 +538,6 @@ verify_heapam(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } -/* - * Check that a relation's relkind and access method are both supported. - */ -static void -sanity_check_relation(Relation rel) -{ - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relam != HEAP_TABLE_AM_OID) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("only heap AM is supported"))); -} - /* * Shared internal implementation for report_corruption and * report_toast_corruption. diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out index 4da28f0a1d..4e325ae56d 100644 --- a/contrib/pageinspect/expected/page.out +++ b/contrib/pageinspect/expected/page.out @@ -180,9 +180,11 @@ DROP TABLE test1; create table test_partitioned (a int) partition by range (a); create index test_partitioned_index on test_partitioned (a); select get_raw_page('test_partitioned', 0); -- error about partitioned table -ERROR: cannot get raw page from partitioned table "test_partitioned" +ERROR: cannot get raw page from relation "test_partitioned" +DETAIL: This operation is not supported for partitioned tables. select get_raw_page('test_partitioned_index', 0); -- error about partitioned index -ERROR: cannot get raw page from partitioned index "test_partitioned_index" +ERROR: cannot get raw page from relation "test_partitioned_index" +DETAIL: This operation is not supported for partitioned indexes. -- a regular table which is a member of a partition set should work though create table test_part1 partition of test_partitioned for values from ( 1 ) to (100); select get_raw_page('test_part1', 0); -- get farther and error about empty table diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c index 7272b21016..e9fee73bc4 100644 --- a/contrib/pageinspect/rawpage.c +++ b/contrib/pageinspect/rawpage.c @@ -155,32 +155,12 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno) relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); - /* Check that this relation has storage */ - if (rel->rd_rel->relkind == RELKIND_VIEW) + if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from view \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from composite type \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from foreign table \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from partitioned table \"%s\"", - RelationGetRelationName(rel)))); - if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from partitioned index \"%s\"", - RelationGetRelationName(rel)))); + errmsg("cannot get raw page from relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); /* * Reject attempts to read non-local temporary relations; we would be diff --git a/contrib/pg_surgery/expected/heap_surgery.out b/contrib/pg_surgery/expected/heap_surgery.out index d4a757ffa0..df7d13b090 100644 --- a/contrib/pg_surgery/expected/heap_surgery.out +++ b/contrib/pg_surgery/expected/heap_surgery.out @@ -170,9 +170,11 @@ rollback; -- check that it fails on an unsupported relkind create view vw as select 1; select heap_force_kill('vw'::regclass, ARRAY['(0, 1)']::tid[]); -ERROR: "vw" is not a table, materialized view, or TOAST table +ERROR: cannot operate on relation "vw" +DETAIL: This operation is not supported for views. select heap_force_freeze('vw'::regclass, ARRAY['(0, 1)']::tid[]); -ERROR: "vw" is not a table, materialized view, or TOAST table +ERROR: cannot operate on relation "vw" +DETAIL: This operation is not supported for views. -- cleanup. drop view vw; drop extension pg_surgery; diff --git a/contrib/pg_surgery/heap_surgery.c b/contrib/pg_surgery/heap_surgery.c index d31e5f31fd..7edfe4f326 100644 --- a/contrib/pg_surgery/heap_surgery.c +++ b/contrib/pg_surgery/heap_surgery.c @@ -37,7 +37,6 @@ static int32 tidcmp(const void *a, const void *b); static Datum heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt); static void sanity_check_tid_array(ArrayType *ta, int *ntids); -static void sanity_check_relation(Relation rel); static BlockNumber find_tids_one_page(ItemPointer tids, int ntids, OffsetNumber *next_start_ptr); @@ -101,8 +100,28 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt) rel = relation_open(relid, RowExclusiveLock); - /* Check target relation. */ - sanity_check_relation(rel); + /* + * Check target relation. + */ + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_MATVIEW && + rel->rd_rel->relkind != RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot operate on relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); + + if (rel->rd_rel->relam != HEAP_TABLE_AM_OID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only heap AM is supported"))); + + /* Must be owner of the table or superuser. */ + if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, + get_relkind_objtype(rel->rd_rel->relkind), + RelationGetRelationName(rel)); tids = ((ItemPointer) ARR_DATA_PTR(ta)); @@ -363,35 +382,6 @@ sanity_check_tid_array(ArrayType *ta, int *ntids) *ntids = ArrayGetNItems(ARR_NDIM(ta), ARR_DIMS(ta)); } -/*------------------------------------------------------------------------- - * sanity_check_relation() - * - * Perform sanity checks on the given relation. - * ------------------------------------------------------------------------ - */ -static void -sanity_check_relation(Relation rel) -{ - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); - - if (rel->rd_rel->relam != HEAP_TABLE_AM_OID) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("only heap AM is supported"))); - - /* Must be owner of the table or superuser. */ - if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, - get_relkind_objtype(rel->rd_rel->relkind), - RelationGetRelationName(rel)); -} - /*------------------------------------------------------------------------- * find_tids_one_page() * diff --git a/contrib/pg_visibility/expected/pg_visibility.out b/contrib/pg_visibility/expected/pg_visibility.out index 315633bfea..9de54db2a2 100644 --- a/contrib/pg_visibility/expected/pg_visibility.out +++ b/contrib/pg_visibility/expected/pg_visibility.out @@ -41,66 +41,91 @@ ROLLBACK; create table test_partitioned (a int) partition by list (a); -- these should all fail select pg_visibility('test_partitioned', 0); -ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +ERROR: relation "test_partitioned" is of wrong relation kind +DETAIL: This operation is not supported for partitioned tables. select pg_visibility_map('test_partitioned'); -ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +ERROR: relation "test_partitioned" is of wrong relation kind +DETAIL: This operation is not supported for partitioned tables. select pg_visibility_map_summary('test_partitioned'); -ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +ERROR: relation "test_partitioned" is of wrong relation kind +DETAIL: This operation is not supported for partitioned tables. select pg_check_frozen('test_partitioned'); -ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +ERROR: relation "test_partitioned" is of wrong relation kind +DETAIL: This operation is not supported for partitioned tables. select pg_truncate_visibility_map('test_partitioned'); -ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +ERROR: relation "test_partitioned" is of wrong relation kind +DETAIL: This operation is not supported for partitioned tables. create table test_partition partition of test_partitioned for values in (1); create index test_index on test_partition (a); -- indexes do not, so these all fail select pg_visibility('test_index', 0); -ERROR: "test_index" is not a table, materialized view, or TOAST table +ERROR: relation "test_index" is of wrong relation kind +DETAIL: This operation is not supported for indexes. select pg_visibility_map('test_index'); -ERROR: "test_index" is not a table, materialized view, or TOAST table +ERROR: relation "test_index" is of wrong relation kind +DETAIL: This operation is not supported for indexes. select pg_visibility_map_summary('test_index'); -ERROR: "test_index" is not a table, materialized view, or TOAST table +ERROR: relation "test_index" is of wrong relation kind +DETAIL: This operation is not supported for indexes. select pg_check_frozen('test_index'); -ERROR: "test_index" is not a table, materialized view, or TOAST table +ERROR: relation "test_index" is of wrong relation kind +DETAIL: This operation is not supported for indexes. select pg_truncate_visibility_map('test_index'); -ERROR: "test_index" is not a table, materialized view, or TOAST table +ERROR: relation "test_index" is of wrong relation kind +DETAIL: This operation is not supported for indexes. create view test_view as select 1; -- views do not have VMs, so these all fail select pg_visibility('test_view', 0); -ERROR: "test_view" is not a table, materialized view, or TOAST table +ERROR: relation "test_view" is of wrong relation kind +DETAIL: This operation is not supported for views. select pg_visibility_map('test_view'); -ERROR: "test_view" is not a table, materialized view, or TOAST table +ERROR: relation "test_view" is of wrong relation kind +DETAIL: This operation is not supported for views. select pg_visibility_map_summary('test_view'); -ERROR: "test_view" is not a table, materialized view, or TOAST table +ERROR: relation "test_view" is of wrong relation kind +DETAIL: This operation is not supported for views. select pg_check_frozen('test_view'); -ERROR: "test_view" is not a table, materialized view, or TOAST table +ERROR: relation "test_view" is of wrong relation kind +DETAIL: This operation is not supported for views. select pg_truncate_visibility_map('test_view'); -ERROR: "test_view" is not a table, materialized view, or TOAST table +ERROR: relation "test_view" is of wrong relation kind +DETAIL: This operation is not supported for views. create sequence test_sequence; -- sequences do not have VMs, so these all fail select pg_visibility('test_sequence', 0); -ERROR: "test_sequence" is not a table, materialized view, or TOAST table +ERROR: relation "test_sequence" is of wrong relation kind +DETAIL: This operation is not supported for sequences. select pg_visibility_map('test_sequence'); -ERROR: "test_sequence" is not a table, materialized view, or TOAST table +ERROR: relation "test_sequence" is of wrong relation kind +DETAIL: This operation is not supported for sequences. select pg_visibility_map_summary('test_sequence'); -ERROR: "test_sequence" is not a table, materialized view, or TOAST table +ERROR: relation "test_sequence" is of wrong relation kind +DETAIL: This operation is not supported for sequences. select pg_check_frozen('test_sequence'); -ERROR: "test_sequence" is not a table, materialized view, or TOAST table +ERROR: relation "test_sequence" is of wrong relation kind +DETAIL: This operation is not supported for sequences. select pg_truncate_visibility_map('test_sequence'); -ERROR: "test_sequence" is not a table, materialized view, or TOAST table +ERROR: relation "test_sequence" is of wrong relation kind +DETAIL: This operation is not supported for sequences. create foreign data wrapper dummy; create server dummy_server foreign data wrapper dummy; create foreign table test_foreign_table () server dummy_server; -- foreign tables do not have VMs, so these all fail select pg_visibility('test_foreign_table', 0); -ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +ERROR: relation "test_foreign_table" is of wrong relation kind +DETAIL: This operation is not supported for foreign tables. select pg_visibility_map('test_foreign_table'); -ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +ERROR: relation "test_foreign_table" is of wrong relation kind +DETAIL: This operation is not supported for foreign tables. select pg_visibility_map_summary('test_foreign_table'); -ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +ERROR: relation "test_foreign_table" is of wrong relation kind +DETAIL: This operation is not supported for foreign tables. select pg_check_frozen('test_foreign_table'); -ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +ERROR: relation "test_foreign_table" is of wrong relation kind +DETAIL: This operation is not supported for foreign tables. select pg_truncate_visibility_map('test_foreign_table'); -ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +ERROR: relation "test_foreign_table" is of wrong relation kind +DETAIL: This operation is not supported for foreign tables. -- check some of the allowed relkinds create table regular_table (a int, b text); alter table regular_table alter column b set storage external; diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c index dd0c124e62..c6d983183d 100644 --- a/contrib/pg_visibility/pg_visibility.c +++ b/contrib/pg_visibility/pg_visibility.c @@ -781,6 +781,7 @@ check_relation_relkind(Relation rel) rel->rd_rel->relkind != RELKIND_TOASTVALUE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); + errmsg("relation \"%s\" is of wrong relation kind", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); } diff --git a/contrib/pgstattuple/expected/pgstattuple.out b/contrib/pgstattuple/expected/pgstattuple.out index 40f7825ddb..e4ac86f9e3 100644 --- a/contrib/pgstattuple/expected/pgstattuple.out +++ b/contrib/pgstattuple/expected/pgstattuple.out @@ -155,13 +155,17 @@ create table test_partitioned (a int) partition by range (a); create index test_partitioned_index on test_partitioned(a); -- these should all fail select pgstattuple('test_partitioned'); -ERROR: "test_partitioned" (partitioned table) is not supported +ERROR: cannot get tuple-level statistics for relation "test_partitioned" +DETAIL: This operation is not supported for partitioned tables. select pgstattuple('test_partitioned_index'); -ERROR: "test_partitioned_index" (partitioned index) is not supported +ERROR: cannot get tuple-level statistics for relation "test_partitioned_index" +DETAIL: This operation is not supported for partitioned indexes. select pgstattuple_approx('test_partitioned'); -ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +ERROR: relation "test_partitioned" is of wrong relation kind +DETAIL: This operation is not supported for partitioned tables. select pg_relpages('test_partitioned'); -ERROR: "test_partitioned" is not a table, index, materialized view, sequence, or TOAST table +ERROR: cannot get page count of relation "test_partitioned" +DETAIL: This operation is not supported for partitioned tables. select pgstatindex('test_partitioned'); ERROR: relation "test_partitioned" is not a btree index select pgstatginindex('test_partitioned'); @@ -171,11 +175,14 @@ ERROR: "test_partitioned" is not an index create view test_view as select 1; -- these should all fail select pgstattuple('test_view'); -ERROR: "test_view" (view) is not supported +ERROR: cannot get tuple-level statistics for relation "test_view" +DETAIL: This operation is not supported for views. select pgstattuple_approx('test_view'); -ERROR: "test_view" is not a table, materialized view, or TOAST table +ERROR: relation "test_view" is of wrong relation kind +DETAIL: This operation is not supported for views. select pg_relpages('test_view'); -ERROR: "test_view" is not a table, index, materialized view, sequence, or TOAST table +ERROR: cannot get page count of relation "test_view" +DETAIL: This operation is not supported for views. select pgstatindex('test_view'); ERROR: relation "test_view" is not a btree index select pgstatginindex('test_view'); @@ -187,11 +194,14 @@ create server dummy_server foreign data wrapper dummy; create foreign table test_foreign_table () server dummy_server; -- these should all fail select pgstattuple('test_foreign_table'); -ERROR: "test_foreign_table" (foreign table) is not supported +ERROR: cannot get tuple-level statistics for relation "test_foreign_table" +DETAIL: This operation is not supported for foreign tables. select pgstattuple_approx('test_foreign_table'); -ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +ERROR: relation "test_foreign_table" is of wrong relation kind +DETAIL: This operation is not supported for foreign tables. select pg_relpages('test_foreign_table'); -ERROR: "test_foreign_table" is not a table, index, materialized view, sequence, or TOAST table +ERROR: cannot get page count of relation "test_foreign_table" +DETAIL: This operation is not supported for foreign tables. select pgstatindex('test_foreign_table'); ERROR: relation "test_foreign_table" is not a btree index select pgstatginindex('test_foreign_table'); diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c index 1fe193bb25..3b836f370e 100644 --- a/contrib/pgstattuple/pgstatapprox.c +++ b/contrib/pgstattuple/pgstatapprox.c @@ -289,8 +289,9 @@ pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo) rel->rd_rel->relkind == RELKIND_TOASTVALUE)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); + errmsg("relation \"%s\" is of wrong relation kind", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); if (rel->rd_rel->relam != HEAP_TABLE_AM_OID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index 5368bb30f0..6c4b053dd0 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -128,8 +128,8 @@ typedef struct HashIndexStat } HashIndexStat; static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo); +static int64 pg_relpages_impl(Relation rel); static void GetHashPageStats(Page page, HashIndexStat *stats); -static void check_relation_relkind(Relation rel); /* ------------------------------------------------------ * pgstatindex() @@ -383,7 +383,6 @@ Datum pg_relpages(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_PP(0); - int64 relpages; Relation rel; RangeVar *relrv; @@ -395,16 +394,7 @@ pg_relpages(PG_FUNCTION_ARGS) relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); - /* only some relkinds have storage */ - check_relation_relkind(rel); - - /* note: this will work OK on non-local temp tables */ - - relpages = RelationGetNumberOfBlocks(rel); - - relation_close(rel, AccessShareLock); - - PG_RETURN_INT64(relpages); + PG_RETURN_INT64(pg_relpages_impl(rel)); } /* No need for superuser checks in v1.5, see above */ @@ -412,23 +402,13 @@ Datum pg_relpages_v1_5(PG_FUNCTION_ARGS) { text *relname = PG_GETARG_TEXT_PP(0); - int64 relpages; Relation rel; RangeVar *relrv; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); - /* only some relkinds have storage */ - check_relation_relkind(rel); - - /* note: this will work OK on non-local temp tables */ - - relpages = RelationGetNumberOfBlocks(rel); - - relation_close(rel, AccessShareLock); - - PG_RETURN_INT64(relpages); + PG_RETURN_INT64(pg_relpages_impl(rel)); } /* Must keep superuser() check, see above. */ @@ -436,7 +416,6 @@ Datum pg_relpagesbyid(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); - int64 relpages; Relation rel; if (!superuser()) @@ -446,16 +425,7 @@ pg_relpagesbyid(PG_FUNCTION_ARGS) rel = relation_open(relid, AccessShareLock); - /* only some relkinds have storage */ - check_relation_relkind(rel); - - /* note: this will work OK on non-local temp tables */ - - relpages = RelationGetNumberOfBlocks(rel); - - relation_close(rel, AccessShareLock); - - PG_RETURN_INT64(relpages); + PG_RETURN_INT64(pg_relpages_impl(rel)); } /* No need for superuser checks in v1.5, see above */ @@ -463,13 +433,24 @@ Datum pg_relpagesbyid_v1_5(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); - int64 relpages; Relation rel; rel = relation_open(relid, AccessShareLock); - /* only some relkinds have storage */ - check_relation_relkind(rel); + PG_RETURN_INT64(pg_relpages_impl(rel)); +} + +static int64 +pg_relpages_impl(Relation rel) +{ + int64 relpages; + + if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get page count of relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); /* note: this will work OK on non-local temp tables */ @@ -477,7 +458,7 @@ pg_relpagesbyid_v1_5(PG_FUNCTION_ARGS) relation_close(rel, AccessShareLock); - PG_RETURN_INT64(relpages); + return relpages; } /* ------------------------------------------------------ @@ -754,21 +735,3 @@ GetHashPageStats(Page page, HashIndexStat *stats) } stats->free_space += PageGetExactFreeSpace(page); } - -/* - * check_relation_relkind - convenience routine to check that relation - * is of the relkind supported by the callers - */ -static void -check_relation_relkind(Relation rel) -{ - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_INDEX && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_SEQUENCE && - rel->rd_rel->relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, index, materialized view, sequence, or TOAST table", - RelationGetRelationName(rel)))); -} diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index 21fdeff8af..f408e6d84d 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -284,31 +284,20 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo) err = "unknown index"; break; } + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("index \"%s\" (%s) is not supported", + RelationGetRelationName(rel), err))); break; - case RELKIND_VIEW: - err = "view"; - break; - case RELKIND_COMPOSITE_TYPE: - err = "composite type"; - break; - case RELKIND_FOREIGN_TABLE: - err = "foreign table"; - break; - case RELKIND_PARTITIONED_TABLE: - err = "partitioned table"; - break; - case RELKIND_PARTITIONED_INDEX: - err = "partitioned index"; - break; + default: - err = "unknown"; - break; + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot get tuple-level statistics for relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); } - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("\"%s\" (%s) is not supported", - RelationGetRelationName(rel), err))); return 0; /* should not happen */ } diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 69f9dd51a7..d297e77361 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -26,6 +26,7 @@ OBJS = \ partition.o \ pg_aggregate.o \ pg_cast.o \ + pg_class.o \ pg_collation.o \ pg_constraint.o \ pg_conversion.o \ diff --git a/src/backend/catalog/pg_class.c b/src/backend/catalog/pg_class.c new file mode 100644 index 0000000000..304c0af808 --- /dev/null +++ b/src/backend/catalog/pg_class.c @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- + * + * pg_class.c + * routines to support manipulation of the pg_class relation + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/catalog/pg_class.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/pg_class.h" + +/* + * Issue an errdetail() informing that the relkind is not supported for this + * operation. + */ +int +errdetail_relkind_not_supported(char relkind) +{ + switch (relkind) + { + case RELKIND_RELATION: + return errdetail("This operation is not supported for tables."); + case RELKIND_INDEX: + return errdetail("This operation is not supported for indexes."); + case RELKIND_SEQUENCE: + return errdetail("This operation is not supported for sequences."); + case RELKIND_TOASTVALUE: + return errdetail("This operation is not supported for TOAST tables."); + case RELKIND_VIEW: + return errdetail("This operation is not supported for views."); + case RELKIND_MATVIEW: + return errdetail("This operation is not supported for materialized views."); + case RELKIND_COMPOSITE_TYPE: + return errdetail("This operation is not supported for composite types."); + case RELKIND_FOREIGN_TABLE: + return errdetail("This operation is not supported for foreign tables."); + case RELKIND_PARTITIONED_TABLE: + return errdetail("This operation is not supported for partitioned tables."); + case RELKIND_PARTITIONED_INDEX: + return errdetail("This operation is not supported for partitioned indexes."); + default: + elog(ERROR, "unrecognized relkind: '%c'", relkind); + return 0; + } +} diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index bf81f6ccc5..147b5abc19 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -99,10 +99,8 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_MATVIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or materialized view", - relName))); + elog(ERROR, "\"%s\" is not a table or materialized view", + relName); /* create_toast_table does all the work */ if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0, diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 216b8d3068..834f1a2a3f 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -98,8 +98,9 @@ CommentObject(CommentStmt *stmt) relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", - RelationGetRelationName(relation)))); + errmsg("cannot set comment on relation \"%s\"", + RelationGetRelationName(relation)), + errdetail_relkind_not_supported(relation->rd_rel->relkind))); break; default: break; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 76774dce06..c14ca27c5e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -650,22 +650,12 @@ DefineIndex(Oid relationId, case RELKIND_PARTITIONED_TABLE: /* OK */ break; - case RELKIND_FOREIGN_TABLE: - - /* - * Custom error message for FOREIGN TABLE since the term is close - * to a regular table and can confuse the user. - */ - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot create index on foreign table \"%s\"", - RelationGetRelationName(rel)))); - break; default: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or materialized view", - RelationGetRelationName(rel)))); + errmsg("cannot create index on relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); break; } diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index 34f2270ced..62465bacd8 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -89,8 +89,9 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid, relkind != RELKIND_VIEW) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", - rv->relname))); + errmsg("cannot lock relation \"%s\"", + rv->relname), + errdetail_relkind_not_supported(relkind))); /* * Make note if a temporary relation has been accessed in this diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index 6906714298..ddc019cb39 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -188,8 +188,9 @@ ExecSecLabelStmt(SecLabelStmt *stmt) relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", - RelationGetRelationName(relation)))); + errmsg("cannot set security label on relation \"%s\"", + RelationGetRelationName(relation)), + errdetail_relkind_not_supported(relation->rd_rel->relkind))); break; default: break; diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0415df9ccb..e3f9f6d53d 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1680,8 +1680,9 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity) tablerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("referenced relation \"%s\" is not a table or foreign table", - RelationGetRelationName(tablerel)))); + errmsg("sequence cannot be owned by relation \"%s\"", + RelationGetRelationName(tablerel)), + errdetail_relkind_not_supported(tablerel->rd_rel->relkind))); /* We insist on same owner and schema */ if (seqrel->rd_rel->relowner != tablerel->rd_rel->relowner) diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index b244a0fbd7..4856f4b41d 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -135,8 +135,9 @@ CreateStatistics(CreateStatsStmt *stmt) rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("relation \"%s\" is not a table, foreign table, or materialized view", - RelationGetRelationName(rel)))); + errmsg("cannot define statistics for relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); /* You must own the relation to create stats on it */ if (!pg_class_ownercheck(RelationGetRelid(rel), stxowner)) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 97a9725df7..03dfd2e7fa 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -398,8 +398,7 @@ static void ATRewriteTables(AlterTableStmt *parsetree, AlterTableUtilityContext *context); static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode); static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); -static void ATSimplePermissions(Relation rel, int allowed_targets); -static void ATWrongRelkindError(Relation rel, int allowed_targets); +static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets); static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context); @@ -3394,8 +3393,9 @@ renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing) relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, index, or foreign table", - NameStr(classform->relname)))); + errmsg("cannot rename columns of relation \"%s\"", + NameStr(classform->relname)), + errdetail_relkind_not_supported(relkind))); /* * permissions checking. only the owner of a class can change its schema. @@ -4422,7 +4422,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, switch (cmd->subtype) { case AT_AddColumn: /* ADD COLUMN */ - ATSimplePermissions(rel, + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE); ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd, lockmode, context); @@ -4430,7 +4430,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_COL; break; case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */ - ATSimplePermissions(rel, ATT_VIEW); + ATSimplePermissions(cmd->subtype, rel, ATT_VIEW); ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd, lockmode, context); /* Recursion occurs during execution phase */ @@ -4444,7 +4444,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, * substitutes default values into INSERTs before it expands * rules. */ - ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); /* No command-specific prep needed */ pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP; @@ -4452,77 +4452,77 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_CookedColumnDefault: /* add a pre-cooked default */ /* This is currently used only in CREATE TABLE */ /* (so the permission check really isn't necessary) */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* This command never recurses */ pass = AT_PASS_ADD_OTHERCONSTR; break; case AT_AddIdentity: - ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); /* This command never recurses */ pass = AT_PASS_ADD_OTHERCONSTR; break; case AT_SetIdentity: - ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); /* This command never recurses */ /* This should run after AddIdentity, so do it in MISC pass */ pass = AT_PASS_MISC; break; case AT_DropIdentity: - ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); /* This command never recurses */ pass = AT_PASS_DROP; break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATPrepDropNotNull(rel, recurse, recursing); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); pass = AT_PASS_DROP; break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Need command-specific recursion decision */ ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing, lockmode, context); pass = AT_PASS_COL_ATTRS; break; case AT_CheckNotNull: /* check column is already marked NOT NULL */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); /* No command-specific prep needed */ pass = AT_PASS_COL_ATTRS; break; case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode); pass = AT_PASS_DROP; break; case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */ - ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_SetOptions: /* ALTER COLUMN SET ( options ) */ case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */ - ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE); /* This command never recurses */ pass = AT_PASS_MISC; break; case AT_SetStorage: /* ALTER COLUMN SET STORAGE */ - ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */ - ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_DropColumn: /* DROP COLUMN */ - ATSimplePermissions(rel, + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE); ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd, lockmode, context); @@ -4530,13 +4530,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_DROP; break; case AT_AddIndex: /* ADD INDEX */ - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_ADD_INDEX; break; case AT_AddConstraint: /* ADD CONSTRAINT */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) @@ -4544,13 +4544,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_CONSTR; break; case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */ - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_ADD_INDEXCONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATCheckPartitionsNotInUse(rel, lockmode); /* Other recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ @@ -4559,7 +4559,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ - ATSimplePermissions(rel, + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE); /* See comments for ATPrepAlterColumnType */ cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode, @@ -4571,7 +4571,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ALTER_TYPE; break; case AT_AlterColumnGenericOptions: - ATSimplePermissions(rel, ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; @@ -4583,13 +4583,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, break; case AT_ClusterOn: /* CLUSTER ON */ case AT_DropCluster: /* SET WITHOUT CLUSTER */ - ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_SetLogged: /* SET LOGGED */ - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); if (tab->chgPersistence) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -4604,7 +4604,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_MISC; break; case AT_SetUnLogged: /* SET UNLOGGED */ - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); if (tab->chgPersistence) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -4619,11 +4619,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_MISC; break; case AT_DropOids: /* SET WITHOUT OIDS */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); pass = AT_PASS_DROP; break; case AT_SetTableSpace: /* SET TABLESPACE */ - ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX); /* This command never recurses */ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode); @@ -4632,30 +4632,30 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_SetRelOptions: /* SET (...) */ case AT_ResetRelOptions: /* RESET (...) */ case AT_ReplaceRelOptions: /* reset them all, then set just these */ - ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_AddInherit: /* INHERIT */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* This command never recurses */ ATPrepAddInherit(rel); pass = AT_PASS_MISC; break; case AT_DropInherit: /* NO INHERIT */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_AlterConstraint: /* ALTER CONSTRAINT */ - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); /* Recursion occurs during execution phase */ pass = AT_PASS_MISC; break; case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */ - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) @@ -4663,7 +4663,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_MISC; break; case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */ - ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW); pass = AT_PASS_MISC; /* This command never recurses */ /* No command-specific prep needed */ @@ -4676,7 +4676,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_DisableTrig: /* DISABLE TRIGGER variants */ case AT_DisableTrigAll: case AT_DisableTrigUser: - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context); pass = AT_PASS_MISC; @@ -4691,28 +4691,28 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_DisableRowSecurity: case AT_ForceRowSecurity: case AT_NoForceRowSecurity: - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_GenericOptions: - ATSimplePermissions(rel, ATT_FOREIGN_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_AttachPartition: - ATSimplePermissions(rel, ATT_TABLE | ATT_PARTITIONED_INDEX); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_DetachPartition: - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_DetachPartitionFinalize: - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(cmd->subtype, rel, ATT_TABLE); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; @@ -5941,6 +5941,145 @@ ATGetQueueEntry(List **wqueue, Relation rel) return tab; } +static const char * +alter_table_type_to_string(AlterTableType cmdtype) +{ + switch (cmdtype) + { + case AT_AddColumn: + case AT_AddColumnRecurse: + case AT_AddColumnToView: + return "ADD COLUMN"; + case AT_ColumnDefault: + case AT_CookedColumnDefault: + return "ALTER COLUMN ... SET DEFAULT"; + case AT_DropNotNull: + return "ALTER COLUMN ... DROP NOT NULL"; + case AT_SetNotNull: + return "ALTER COLUMN ... SET NOT NULL"; + case AT_DropExpression: + return "ALTER COLUMN ... DROP EXPRESSION"; + case AT_CheckNotNull: + return NULL; /* not real grammar */ + case AT_SetStatistics: + return "ALTER COLUMN ... SET STATISTICS"; + case AT_SetOptions: + return "ALTER COLUMN ... SET"; + case AT_ResetOptions: + return "ALTER COLUMN ... RESET"; + case AT_SetStorage: + return "ALTER COLUMN ... SET STORAGE"; + case AT_SetCompression: + return "ALTER COLUMN ... SET COMPRESSION"; + case AT_DropColumn: + case AT_DropColumnRecurse: + return "DROP COLUMN"; + case AT_AddIndex: + case AT_ReAddIndex: + return NULL; /* not real grammar */ + case AT_AddConstraint: + case AT_AddConstraintRecurse: + case AT_ReAddConstraint: + case AT_ReAddDomainConstraint: + case AT_AddIndexConstraint: + return "ADD CONSTRAINT"; + case AT_AlterConstraint: + return "ALTER CONSTRAINT"; + case AT_ValidateConstraint: + case AT_ValidateConstraintRecurse: + return "VALIDATE CONSTRAINT"; + case AT_DropConstraint: + case AT_DropConstraintRecurse: + return "DROP CONSTRAINT"; + case AT_ReAddComment: + return NULL; /* not real grammar */ + case AT_AlterColumnType: + return "ALTER COLUMN ... SET DATA TYPE"; + case AT_AlterColumnGenericOptions: + return "ALTER COLUMN ... OPTIONS"; + case AT_ChangeOwner: + return "OWNER TO"; + case AT_ClusterOn: + return "CLUSTER ON"; + case AT_DropCluster: + return "SET WITHOUT CLUSTER"; + case AT_SetLogged: + return "SET LOGGED"; + case AT_SetUnLogged: + return "SET UNLOGGED"; + case AT_DropOids: + return "SET WITHOUT OIDS"; + case AT_SetTableSpace: + return "SET TABLESPACE"; + case AT_SetRelOptions: + return "SET"; + case AT_ResetRelOptions: + return "RESET"; + case AT_ReplaceRelOptions: + return NULL; /* not real grammar */ + case AT_EnableTrig: + return "ENABLE TRIGGER"; + case AT_EnableAlwaysTrig: + return "ENABLE ALWAYS TRIGGER"; + case AT_EnableReplicaTrig: + return "ENABLE REPLICA TRIGGER"; + case AT_DisableTrig: + return "DISABLE TRIGGER"; + case AT_EnableTrigAll: + return "ENABLE TRIGGER ALL"; + case AT_DisableTrigAll: + return "DISABLE TRIGGER ALL"; + case AT_EnableTrigUser: + return "ENABLE TRIGGER USER"; + case AT_DisableTrigUser: + return "DISABLE TRIGGER USER"; + case AT_EnableRule: + return "ENABLE RULE"; + case AT_EnableAlwaysRule: + return "ENABLE ALWAYS RULE"; + case AT_EnableReplicaRule: + return "ENABLE REPLICA RULE"; + case AT_DisableRule: + return "DISABLE RULE"; + case AT_AddInherit: + return "INHERIT"; + case AT_DropInherit: + return "NO INHERIT"; + case AT_AddOf: + return "OF"; + case AT_DropOf: + return "NOT OF"; + case AT_ReplicaIdentity: + return "REPLICA IDENTITY"; + case AT_EnableRowSecurity: + return "ENABLE ROW SECURITY"; + case AT_DisableRowSecurity: + return "DISABLE ROW SECURITY"; + case AT_ForceRowSecurity: + return "FORCE ROW SECURITY"; + case AT_NoForceRowSecurity: + return "NO FORCE ROW SECURITY"; + case AT_GenericOptions: + return "OPTIONS"; + case AT_AttachPartition: + return "ATTACH PARTITION"; + case AT_DetachPartition: + return "DETACH PARTITION"; + case AT_DetachPartitionFinalize: + return "DETACH PARTITION ... FINALIZE"; + case AT_AddIdentity: + return "ALTER COLUMN ... ADD IDENTITY"; + case AT_SetIdentity: + return "ALTER COLUMN ... SET"; + case AT_DropIdentity: + return "ALTER COLUMN ... DROP IDENTITY"; + case AT_ReAddStatistics: + return NULL; /* not real grammar */ + } + + return NULL; +} + /* * ATSimplePermissions * @@ -5949,7 +6088,7 @@ ATGetQueueEntry(List **wqueue, Relation rel) * - Ensure that it is not a system table */ static void -ATSimplePermissions(Relation rel, int allowed_targets) +ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets) { int actual_target; @@ -5984,7 +6123,21 @@ ATSimplePermissions(Relation rel, int allowed_targets) /* Wrong target type? */ if ((actual_target & allowed_targets) == 0) - ATWrongRelkindError(rel, allowed_targets); + { + const char *action_str = alter_table_type_to_string(cmdtype); + + if (action_str) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + /* translator: %s is a group of some SQL keywords */ + errmsg("ALTER action %s cannot be performed on relation \"%s\"", + action_str, RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); + else + /* internal error? */ + elog(ERROR, "invalid ALTER action attempted on relation \"%s\"", + RelationGetRelationName(rel)); + } /* Permissions checks */ if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) @@ -5998,66 +6151,6 @@ ATSimplePermissions(Relation rel, int allowed_targets) RelationGetRelationName(rel)))); } -/* - * ATWrongRelkindError - * - * Throw an error when a relation has been determined to be of the wrong - * type. - */ -static void -ATWrongRelkindError(Relation rel, int allowed_targets) -{ - char *msg; - - switch (allowed_targets) - { - case ATT_TABLE: - msg = _("\"%s\" is not a table"); - break; - case ATT_TABLE | ATT_VIEW: - msg = _("\"%s\" is not a table or view"); - break; - case ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE: - msg = _("\"%s\" is not a table, view, or foreign table"); - break; - case ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX: - msg = _("\"%s\" is not a table, view, materialized view, or index"); - break; - case ATT_TABLE | ATT_MATVIEW: - msg = _("\"%s\" is not a table or materialized view"); - break; - case ATT_TABLE | ATT_MATVIEW | ATT_INDEX: - msg = _("\"%s\" is not a table, materialized view, or index"); - break; - case ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE: - msg = _("\"%s\" is not a table, materialized view, or foreign table"); - break; - case ATT_TABLE | ATT_FOREIGN_TABLE: - msg = _("\"%s\" is not a table or foreign table"); - break; - case ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE: - msg = _("\"%s\" is not a table, composite type, or foreign table"); - break; - case ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_FOREIGN_TABLE: - msg = _("\"%s\" is not a table, materialized view, index, or foreign table"); - break; - case ATT_VIEW: - msg = _("\"%s\" is not a view"); - break; - case ATT_FOREIGN_TABLE: - msg = _("\"%s\" is not a foreign table"); - break; - default: - /* shouldn't get here, add all necessary cases above */ - msg = _("\"%s\" is of the wrong type"); - break; - } - - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg(msg, RelationGetRelationName(rel)))); -} - /* * ATSimpleRecursion * @@ -6452,7 +6545,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE); if (rel->rd_rel->relispartition && !recursing) ereport(ERROR, @@ -8186,7 +8279,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Initialize addrs on the first invocation */ Assert(!recursing || addrs != NULL); @@ -8670,7 +8763,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* * Call AddRelationNewConstraints to do the work, making sure it works on @@ -11286,7 +11379,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) - ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE); conrel = table_open(ConstraintRelationId, RowExclusiveLock); @@ -13205,8 +13298,9 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock default: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, sequence, or foreign table", - NameStr(tuple_class->relname)))); + errmsg("cannot change owner of relation \"%s\"", + NameStr(tuple_class->relname)), + errdetail_relkind_not_supported(tuple_class->relkind))); } /* @@ -13621,8 +13715,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, default: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, index, or TOAST table", - RelationGetRelationName(rel)))); + errmsg("cannot set options for relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); break; } @@ -14176,7 +14271,7 @@ 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, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP && @@ -16505,17 +16600,27 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved * to a different schema, such as indexes and TOAST tables. */ - if (IsA(stmt, AlterObjectSchemaStmt) && - relkind != RELKIND_RELATION && - relkind != RELKIND_VIEW && - relkind != RELKIND_MATVIEW && - relkind != RELKIND_SEQUENCE && - relkind != RELKIND_FOREIGN_TABLE && - relkind != RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, sequence, or foreign table", - rv->relname))); + if (IsA(stmt, AlterObjectSchemaStmt)) + { + if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot change schema of index \"%s\"", + rv->relname), + errhint("Change the schema of the table instead."))); + else if (relkind == RELKIND_COMPOSITE_TYPE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot change schema of composite type \"%s\"", + rv->relname), + errhint("Use ALTER TYPE instead."))); + else if (relkind == RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot change schema of TOAST table \"%s\"", + rv->relname), + errhint("Change the schema of the table instead."))); + } ReleaseSysCache(tuple); } @@ -17077,7 +17182,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, * Must be owner of both parent and source table -- parent was checked by * ATSimplePermissions call in ATPrepCmd */ - ATSimplePermissions(attachrel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE); /* A partition can only have one parent */ if (attachrel->rd_rel->relispartition) diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 07c73f39de..952c8d582a 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -286,8 +286,9 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", - RelationGetRelationName(rel)))); + errmsg("relation \"%s\" cannot have triggers", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, @@ -1262,8 +1263,9 @@ RemoveTriggerById(Oid trigOid) rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, or foreign table", - RelationGetRelationName(rel)))); + errmsg("relation \"%s\" cannot have triggers", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, @@ -1368,8 +1370,9 @@ RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid, form->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, or foreign table", - rv->relname))); + errmsg("relation \"%s\" cannot have triggers", + rv->relname), + errdetail_relkind_not_supported(form->relkind))); /* you must own the table to rename one of its triggers */ if (!pg_class_ownercheck(relid, GetUserId())) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 81d3e7990c..3afcd6b511 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -976,8 +976,9 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", - RelationGetRelationName(relation)))); + errmsg("relation \"%s\" is invalid in LIKE clause", + RelationGetRelationName(relation)), + errdetail_relkind_not_supported(relation->rd_rel->relkind))); cancel_parser_errposition_callback(&pcbstate); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 27e4ef911c..6589345ac4 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -268,8 +268,9 @@ DefineQueryRewrite(const char *rulename, event_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", - RelationGetRelationName(event_relation)))); + errmsg("relation \"%s\" cannot have rules", + RelationGetRelationName(event_relation)), + errdetail_relkind_not_supported(event_relation->rd_rel->relkind))); if (!allowSystemTableMods && IsSystemRelation(event_relation)) ereport(ERROR, @@ -935,7 +936,8 @@ RangeVarCallbackForRenameRule(const RangeVar *rv, Oid relid, Oid oldrelid, form->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table or view", rv->relname))); + errmsg("relation \"%s\" cannot have rules", rv->relname), + errdetail_relkind_not_supported(form->relkind))); if (!allowSystemTableMods && IsSystemClass(relid, form)) ereport(ERROR, diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 3458d95606..fef9945ed8 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -198,6 +198,7 @@ DECLARE_INDEX(pg_class_tblspc_relfilenode_index, 3455, ClassTblspcRelfilenodeInd (relkind) == RELKIND_TOASTVALUE || \ (relkind) == RELKIND_MATVIEW) +extern int errdetail_relkind_not_supported(char relkind); #endif /* EXPOSE_TO_CLIENT_CODE */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index f81bdf513b..8dcb00ac67 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -1087,9 +1087,11 @@ ERROR: column "bar" of relation "atacc1" does not exist -- 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 or foreign table +ERROR: ALTER action ALTER COLUMN ... DROP NOT NULL cannot be performed on relation "myview" +DETAIL: This operation is not supported for views. alter table myview alter column test set not null; -ERROR: "myview" is not a table or foreign table +ERROR: ALTER action ALTER COLUMN ... SET NOT NULL cannot be performed on relation "myview" +DETAIL: This operation is not supported for views. drop view myview; drop table atacc1; -- set not null verified by constraints @@ -1387,7 +1389,8 @@ select * from myview; (0 rows) alter table myview drop d; -ERROR: "myview" is not a table, composite type, or foreign table +ERROR: ALTER action DROP COLUMN cannot be performed on relation "myview" +DETAIL: This operation is not supported for views. 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/create_table_like.out b/src/test/regress/expected/create_table_like.out index 4dc5e6aa5f..7ad5fafe93 100644 --- a/src/test/regress/expected/create_table_like.out +++ b/src/test/regress/expected/create_table_like.out @@ -504,9 +504,10 @@ DROP TABLE noinh_con_copy, noinh_con_copy1; CREATE TABLE ctlt4 (a int, b text); CREATE SEQUENCE ctlseq1; CREATE TABLE ctlt10 (LIKE ctlseq1); -- fail -ERROR: "ctlseq1" is not a table, view, materialized view, composite type, or foreign table +ERROR: relation "ctlseq1" is invalid in LIKE clause LINE 1: CREATE TABLE ctlt10 (LIKE ctlseq1); ^ +DETAIL: This operation is not supported for sequences. CREATE VIEW ctlv1 AS SELECT * FROM ctlt4; CREATE TABLE ctlt11 (LIKE ctlv1); CREATE TABLE ctlt11a (LIKE ctlv1 INCLUDING ALL); diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out index 5385f98a0f..809d40a79a 100644 --- a/src/test/regress/expected/foreign_data.out +++ b/src/test/regress/expected/foreign_data.out @@ -740,7 +740,8 @@ FDW options: (delimiter ',', quote '"', "be quoted" 'value') (1 row) CREATE INDEX id_ft1_c2 ON ft1 (c2); -- ERROR -ERROR: cannot create index on foreign table "ft1" +ERROR: cannot create index on relation "ft1" +DETAIL: This operation is not supported for foreign tables. SELECT * FROM ft1; -- ERROR ERROR: foreign-data wrapper "dummy" has no handler EXPLAIN SELECT * FROM ft1; -- ERROR @@ -864,7 +865,8 @@ LINE 1: ALTER FOREIGN TABLE ft1 ADD PRIMARY KEY (c7); ^ ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0) NOT VALID; ALTER FOREIGN TABLE ft1 ALTER CONSTRAINT ft1_c9_check DEFERRABLE; -- ERROR -ERROR: "ft1" is not a table +ERROR: ALTER action ALTER CONSTRAINT cannot be performed on relation "ft1" +DETAIL: This operation is not supported for foreign tables. ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check; ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR ERROR: constraint "no_const" of relation "ft1" does not exist diff --git a/src/test/regress/expected/indexing.out b/src/test/regress/expected/indexing.out index c93f4470c9..193f780191 100644 --- a/src/test/regress/expected/indexing.out +++ b/src/test/regress/expected/indexing.out @@ -137,7 +137,8 @@ select relname, relpartbound from pg_class (2 rows) alter table idxpart_c detach partition idxpart1_c; -ERROR: "idxpart_c" is not a table +ERROR: ALTER action DETACH PARTITION cannot be performed on relation "idxpart_c" +DETAIL: This operation is not supported for partitioned indexes. drop table idxpart; -- If a partition already has an index, don't create a duplicative one create table idxpart (a int, b int) partition by range (a, b); diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 8b928b2888..71c2b0f1df 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -21,7 +21,8 @@ CREATE SEQUENCE sequence_testx OWNED BY nobody; -- nonsense word ERROR: invalid OWNED BY option HINT: Specify OWNED BY table.column or OWNED BY NONE. CREATE SEQUENCE sequence_testx OWNED BY pg_class_oid_index.oid; -- not a table -ERROR: referenced relation "pg_class_oid_index" is not a table or foreign table +ERROR: sequence cannot be owned by relation "pg_class_oid_index" +DETAIL: This operation is not supported for indexes. CREATE SEQUENCE sequence_testx OWNED BY pg_class.relname; -- not same schema ERROR: sequence must be in same schema as table it is linked to CREATE TABLE sequence_test_table (a int); diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out index 8c214d8dfc..62b05c79f9 100644 --- a/src/test/regress/expected/stats_ext.out +++ b/src/test/regress/expected/stats_ext.out @@ -211,14 +211,18 @@ CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b); CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10); CREATE STATISTICS tststats.s1 ON a, b FROM tststats.t; CREATE STATISTICS tststats.s2 ON a, b FROM tststats.ti; -ERROR: relation "ti" is not a table, foreign table, or materialized view +ERROR: cannot define statistics for relation "ti" +DETAIL: This operation is not supported for indexes. CREATE STATISTICS tststats.s3 ON a, b FROM tststats.s; -ERROR: relation "s" is not a table, foreign table, or materialized view +ERROR: cannot define statistics for relation "s" +DETAIL: This operation is not supported for sequences. CREATE STATISTICS tststats.s4 ON a, b FROM tststats.v; -ERROR: relation "v" is not a table, foreign table, or materialized view +ERROR: cannot define statistics for relation "v" +DETAIL: This operation is not supported for views. CREATE STATISTICS tststats.s5 ON a, b FROM tststats.mv; CREATE STATISTICS tststats.s6 ON a, b FROM tststats.ty; -ERROR: relation "ty" is not a table, foreign table, or materialized view +ERROR: cannot define statistics for relation "ty" +DETAIL: This operation is not supported for composite types. CREATE STATISTICS tststats.s7 ON a, b FROM tststats.f; CREATE STATISTICS tststats.s8 ON a, b FROM tststats.pt; CREATE STATISTICS tststats.s9 ON a, b FROM tststats.pt1;