Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.92) (envelope-from ) id 1j7SWy-0000aN-UE for pgsql-hackers@arkaria.postgresql.org; Thu, 27 Feb 2020 23:25:25 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1j7SWx-0000EH-3e for pgsql-hackers@arkaria.postgresql.org; Thu, 27 Feb 2020 23:25:23 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1j7SWw-0000EA-L6 for pgsql-hackers@lists.postgresql.org; Thu, 27 Feb 2020 23:25:22 +0000 Received: from mail-yw1-xc43.google.com ([2607:f8b0:4864:20::c43]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1j7SWs-00016T-G8 for pgsql-hackers@postgresql.org; Thu, 27 Feb 2020 23:25:21 +0000 Received: by mail-yw1-xc43.google.com with SMTP id i190so1515255ywc.2 for ; Thu, 27 Feb 2020 15:25:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=telsasoft-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=zsh2zh3pjsCeKFNhAAFGnuiYDw2e6fS4Dchw37AZKtE=; b=MQv93H5jpaWCkA25GeJZ6gMzqOxexKZqALmKUKB+IhTTcmuoFHRavhVSDZkWf+R7h7 Qymgoxed7U3G/IejnKqaALJ7SDN1LjaxJNq7W58APgyPzIZ3oCqnf1YtUQ+JEBvq+vsC 5wryPqy9aESUbwwxinHyjwwTVXMrj8bUduqWDE20ZE4hyPszaWtz2WFVriQ04mAWhSJe 5IYjsctfx4ysrBGTLaoqcnG18GpQX+eEX0u6KXQ9u13bMyid9A4IV/EiUr/Vd02ova7W TOIXIh2+aY0CBProgqzBCuFLg4Keszsq1SYY8uqSn6+XnOkK+aiY/98Wsrx8L+hsbpyp MjIQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=zsh2zh3pjsCeKFNhAAFGnuiYDw2e6fS4Dchw37AZKtE=; b=J1HKMQia3L/+viq9kL/pAq1gvprPGgGfQUxlPIyEqLUMVBJUAo0f4h/Sa+6HRkfUB6 8qWZnM1nGvx3iJHCCxwhHR8jKJ2GFR1eyf9u18Da9453dTZr5dWM1bDbsjiOrzC/iP+G oQZDG8TNqRNUds8bW33gXFAMfdrsgu1nEdXOa6D3SmrnX5f6789fVYW/UIJdU2teb3Uk fRKgNquGe/SS0lv0A4kEn1+BGDHT1tW3WGqXeFVx1S6hONAfh61B8I3j0f2HN234AP9r JhCF7ENc3BwI8JtnOmIBwP6itfVYWPbRbAxgdah0fBD8iZ8KzRLiQVdWoPJ5q5n9y6uI OOsg== X-Gm-Message-State: APjAAAX0B5s2grCYRYKFt5fZALV+30JmoWIs31RM6Cgj6xByfGezjmxG jwKfpmyKvVKdm0zidx/PAkOQDw== X-Google-Smtp-Source: APXvYqwzC3Xhb8V3qSC3Q54sAQe8xm8vXjj1/WQPXbJLpbYU6bQEBtlEC9CkD/HH3tsujQFdynoSbg== X-Received: by 2002:a25:6ac1:: with SMTP id f184mr1067551ybc.469.1582845916557; Thu, 27 Feb 2020 15:25:16 -0800 (PST) Received: from pryzbyj (charmander.telsasoft.com. [50.244.222.1]) by smtp.gmail.com with ESMTPSA id j23sm3167817ywb.93.2020.02.27.15.25.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 27 Feb 2020 15:25:15 -0800 (PST) Received: by pryzbyj (Postfix, from userid 1000) id 1175E800926; Thu, 27 Feb 2020 17:25:14 -0600 (CST) Date: Thu, 27 Feb 2020 17:25:13 -0600 From: Justin Pryzby To: Robert Haas Cc: Alvaro Herrera , pgsql-hackers@postgresql.org Subject: Re: ALTER INDEX fails on partitioned index Message-ID: <20200227232513.GE29456@telsasoft.com> References: <20190206173212.GA23770@alvherre.pgsql> <201901071923.7r2xflxvncly@alvherre.pgsql> <20191227035157.GD12890@telsasoft.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="l76fUT7nc3MelDdI" Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.24 (2015-08-30) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --l76fUT7nc3MelDdI Content-Type: text/plain; charset=us-ascii Content-Disposition: inline The attached allows CREATE/ALTER to specify reloptions on a partitioned table which are used as defaults for future children. I think that's a desirable behavior, same as for tablespaces. Michael mentioned that ALTER INDEX ONLY doesn't exist, but that's only an issue if ALTER acts recursively, which isn't the case here. The current behavior seems unreasonable: CREATE allows specifying fillfactor, which does nothing, and unable to alter it, either: postgres=# CREATE TABLE tt(i int)PARTITION BY RANGE (i);; CREATE TABLE postgres=# CREATE INDEX ON tt(i)WITH(fillfactor=11); CREATE INDEX postgres=# \d tt ... "tt_i_idx" btree (i) WITH (fillfactor='11') postgres=# ALTER INDEX tt_i_idx SET (fillfactor=12); ERROR: "tt_i_idx" is not a table, view, materialized view, or index Maybe there are other ALTER commands to handle (UNLOGGED currently does nothing on a partitioned table?, STATISTICS, ...). The first patch makes a prettier message, per Robert's suggestion. -- Justin --l76fUT7nc3MelDdI Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0001-More-specific-error-message-when-failing-to-alter.patch" From e5bb363f514d768a4f540d9c82ad5745944b1486 Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Mon, 30 Dec 2019 09:31:03 -0600 Subject: [PATCH v2 1/2] More specific error message when failing to alter a partitioned index.. "..is not an index" is deemed to be unfriendly https://www.postgresql.org/message-id/CA%2BTgmobq8_-DS7qDEmMi-4ARP1_0bkgFEjYfiK97L2eXq%2BQ%2Bnw%40mail.gmail.com --- src/backend/commands/tablecmds.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index b7c8d66..1b271af 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -366,7 +366,7 @@ static void ATRewriteTables(AlterTableStmt *parsetree, 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 ATWrongRelkindError(Relation rel, int allowed_targets, int actual_target); static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context); @@ -5458,7 +5458,7 @@ ATSimplePermissions(Relation rel, int allowed_targets) /* Wrong target type? */ if ((actual_target & allowed_targets) == 0) - ATWrongRelkindError(rel, allowed_targets); + ATWrongRelkindError(rel, allowed_targets, actual_target); /* Permissions checks */ if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) @@ -5479,7 +5479,7 @@ ATSimplePermissions(Relation rel, int allowed_targets) * type. */ static void -ATWrongRelkindError(Relation rel, int allowed_targets) +ATWrongRelkindError(Relation rel, int allowed_targets, int actual_target) { char *msg; @@ -5527,9 +5527,20 @@ ATWrongRelkindError(Relation rel, int allowed_targets) break; } - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg(msg, RelationGetRelationName(rel)))); + if (actual_target == ATT_PARTITIONED_INDEX && + (allowed_targets&ATT_INDEX) && + !(allowed_targets&ATT_PARTITIONED_INDEX)) + /* Add a special errhint for this case, since "is not an index" message is unfriendly */ + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg(msg, RelationGetRelationName(rel)), + // errhint("\"%s\" is a partitioned index", RelationGetRelationName(rel)))); + errhint("operation is not supported on partitioned indexes"))); + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg(msg, RelationGetRelationName(rel)))); + } /* -- 2.7.4 --l76fUT7nc3MelDdI Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0002-Allow-reloptions-on-partitioned-tables-and-indexe.patch" From 5f7f79c4c8e85528e88ffa0b749faad1da4ab4d5 Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Tue, 25 Feb 2020 14:27:09 -0600 Subject: [PATCH v2 2/2] Allow reloptions on partitioned tables and indexes.. ..which provides default values used for future (direct) partitions. See also similar behavior from: dfa60814 - Fix tablespace handling for partitioned indexes ca410302 - Fix tablespace handling for partitioned tables c94e6942 - Don't allocate storage for partitioned tables. --- src/backend/access/common/reloptions.c | 19 +------------------ src/backend/commands/tablecmds.c | 32 +++++++++++++++++++++++--------- src/test/regress/expected/reloptions.out | 29 +++++++++++++++++++++++++++++ src/test/regress/sql/reloptions.sql | 11 +++++++++++ 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 79430d2..a2678e7 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -1108,10 +1108,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, case RELKIND_RELATION: case RELKIND_TOASTVALUE: case RELKIND_MATVIEW: - options = heap_reloptions(classForm->relkind, datum, false); - break; case RELKIND_PARTITIONED_TABLE: - options = partitioned_table_reloptions(datum, false); + options = heap_reloptions(classForm->relkind, datum, false); break; case RELKIND_VIEW: options = view_reloptions(datum, false); @@ -1581,21 +1579,6 @@ build_reloptions(Datum reloptions, bool validate, } /* - * Option parser for partitioned tables - */ -bytea * -partitioned_table_reloptions(Datum reloptions, bool validate) -{ - /* - * There are no options for partitioned tables yet, but this is able to do - * some validation. - */ - return (bytea *) build_reloptions(reloptions, validate, - RELOPT_KIND_PARTITIONED, - 0, NULL, 0); -} - -/* * Option parser for views */ bytea * diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1b271af..675b74d 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -740,17 +740,33 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, /* * Parse and validate reloptions, if any. */ - reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps, - true, false); + + if (stmt->partbound) + { + /* If partitioned, also use reloptions from the immediate parent */ + Datum oldoptions; + bool isnull; + Oid parentoid = linitial_oid(inheritOids); + HeapTuple tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(parentoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", parentoid); + + oldoptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, + &isnull); + + reloptions = transformRelOptions(isnull ? 0 : oldoptions, + stmt->options, NULL, validnsps, true, false); + + ReleaseSysCache(tuple); + } else + reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, + validnsps, true, false); switch (relkind) { case RELKIND_VIEW: (void) view_reloptions(reloptions, true); break; - case RELKIND_PARTITIONED_TABLE: - (void) partitioned_table_reloptions(reloptions, true); - break; default: (void) heap_reloptions(relkind, reloptions, true); } @@ -4155,7 +4171,7 @@ 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(rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; @@ -12693,10 +12709,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, case RELKIND_RELATION: case RELKIND_TOASTVALUE: case RELKIND_MATVIEW: - (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true); - break; case RELKIND_PARTITIONED_TABLE: - (void) partitioned_table_reloptions(newOptions, true); + (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true); break; case RELKIND_VIEW: (void) view_reloptions(newOptions, true); diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out index 44c1304..8fc8b62 100644 --- a/src/test/regress/expected/reloptions.out +++ b/src/test/regress/expected/reloptions.out @@ -222,3 +222,32 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass; {fillfactor=40} (1 row) +-- For partitioned tables/indexes, expect ALTER..SET fillfactor not to recurse, +-- but rather to set a value in the parent table which is used for new children +CREATE TABLE par(i int) PARTITION BY RANGE(i); +CREATE INDEX par_i_idx ON par(i); +CREATE TABLE par1 PARTITION OF par FOR VALUES FROM (1)TO(2); +ALTER TABLE par SET (fillfactor=13); +ALTER INDEX par_i_idx SET (fillfactor=13); +CREATE TABLE par2 PARTITION OF par FOR VALUES FROM (2)TO(3); +\d+ par1 + Table "public.par1" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+---------+--------------+------------- + i | integer | | | | plain | | +Partition of: par FOR VALUES FROM (1) TO (2) +Partition constraint: ((i IS NOT NULL) AND (i >= 1) AND (i < 2)) +Indexes: + "par1_i_idx" btree (i) + +\d+ par2 + Table "public.par2" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+---------+---------+--------------+------------- + i | integer | | | | plain | | +Partition of: par FOR VALUES FROM (2) TO (3) +Partition constraint: ((i IS NOT NULL) AND (i >= 2) AND (i < 3)) +Indexes: + "par2_i_idx" btree (i) WITH (fillfactor='13') +Options: fillfactor=13 + diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql index cac5b0b..804523c 100644 --- a/src/test/regress/sql/reloptions.sql +++ b/src/test/regress/sql/reloptions.sql @@ -128,3 +128,14 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass; CREATE INDEX reloptions_test_idx3 ON reloptions_test (s); ALTER INDEX reloptions_test_idx3 SET (fillfactor=40); SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass; + +-- For partitioned tables/indexes, expect ALTER..SET fillfactor not to recurse, +-- but rather to set a value in the parent table which is used for new children +CREATE TABLE par(i int) PARTITION BY RANGE(i); +CREATE INDEX par_i_idx ON par(i); +CREATE TABLE par1 PARTITION OF par FOR VALUES FROM (1)TO(2); +ALTER TABLE par SET (fillfactor=13); +ALTER INDEX par_i_idx SET (fillfactor=13); +CREATE TABLE par2 PARTITION OF par FOR VALUES FROM (2)TO(3); +\d par1 +\d par2 -- 2.7.4 --l76fUT7nc3MelDdI--