Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jhzfL-0004Ui-2E for pgsql-hackers@arkaria.postgresql.org; Sun, 07 Jun 2020 18:05:03 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1jhzfJ-0002L7-LQ for pgsql-hackers@arkaria.postgresql.org; Sun, 07 Jun 2020 18:05:01 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jhzfJ-0002Kz-6K for pgsql-hackers@lists.postgresql.org; Sun, 07 Jun 2020 18:05:01 +0000 Received: from mail-io1-xd43.google.com ([2607:f8b0:4864:20::d43]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1jhzfB-00065n-NO for pgsql-hackers@lists.postgresql.org; Sun, 07 Jun 2020 18:05:00 +0000 Received: by mail-io1-xd43.google.com with SMTP id i25so1502611iog.0 for ; Sun, 07 Jun 2020 11:04:52 -0700 (PDT) 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:content-transfer-encoding:in-reply-to :user-agent; bh=8Im3ikR8uXCKYahAcvcW+LLUhwLas4GXgyarWCwwuhY=; b=V6y9Z7KwMtGoQMmshLkMycOWRuycT2Ra2y/Eg+HInsQKtW0Gsg5poCS4mNioRRq2Yp TEf8r3gK6Vm7wVmIGbSY1GPKhQjaNDHLyra4K5xYycbIYixVtKjsCxl3C3QUDxYrxF6h tS+a9uY0SFaY+w6aDN7hFv00s8uxFEkyzR/JvgWfaMxonu52tTfCpGLK5eIth2sLP3DU HU3RRTYr/4RRxibmPUFup/SKKIqS/AwF+MnHXaizV2fBlKLOWodOMbL7h8AFUzC4glDw y/PKGlXzk1zWkpE7GgSWxteuNqyx9i4uwFZ4lFfVZRseRRC1q9jQoxgAGFBVujvBRLp4 YYKQ== 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:content-transfer-encoding :in-reply-to:user-agent; bh=8Im3ikR8uXCKYahAcvcW+LLUhwLas4GXgyarWCwwuhY=; b=T6GpPedJ0/SI7MjvIV7xsXgtduTBJGNY8uJLtniitXy0sh4lu1IHId/jrhM912Izn+ iRRC3L/QKEcyU6Gs9NStTkyESy5XmT1fv8LPBVLXH96dPeG3rd3gCjNR6rYYTZ1+dkay cImxFraUwnsiVVov/5z7XiIc3IbPpHdXgHFnUwQ9K1t2klXKe5BhGWGbqbbf6qYQI995 +ejhSUYA8OTTLbATcSSAdmE1xsWOhNL91ZIrl/cLzXAa+mW+JSWoJOc5471+YSMzSfkL dZBqmgVUbnJXMa3U0qflRqbnXFfwjNdRzXaXGzUeFeQXuxpUCj0zZbrOwUJZ6VbteHiy YpIQ== X-Gm-Message-State: AOAM532HvlmoPFt2oacL/3sz2PoSCjDHaoNlMYydj7PQeXb4dMbCnaE9 WUAQuOb2P807TgBSYx5XoATvcA== X-Google-Smtp-Source: ABdhPJyiWjJOG/uQ/BHTjhms7syT1dznT2uvdpuxGglGvuSZf0NA87feB9iUUSTg58tO5/Uu8hIf2Q== X-Received: by 2002:a05:6638:d52:: with SMTP id d18mr19105792jak.21.1591553090743; Sun, 07 Jun 2020 11:04:50 -0700 (PDT) Received: from pryzbyj.telsasoft (charmander.telsasoft.com. [50.244.222.1]) by smtp.gmail.com with ESMTPSA id l138sm6671840ill.63.2020.06.07.11.04.49 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 07 Jun 2020 11:04:49 -0700 (PDT) Received: by pryzbyj.telsasoft (Postfix, from userid 1000) id A03628000D7; Sun, 7 Jun 2020 13:04:48 -0500 (CDT) Date: Sun, 7 Jun 2020 13:04:48 -0500 From: Justin Pryzby To: =?utf-8?B?5p2O5p2wKOaFjui/vSk=?= Cc: pgsql-hackers , =?utf-8?B?5pu+5paH5peMKOS5ieS7jik=?= Subject: Re: how to create index concurrently on paritioned table Message-ID: <20200607180448.GL22473@telsasoft.com> References: <20200606142332.GH22473@telsasoft.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="aVD9QWMuhilNxW9f" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20200606142332.GH22473@telsasoft.com> User-Agent: Mutt/1.9.4 (2018-02-28) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --aVD9QWMuhilNxW9f Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit On Sat, Jun 06, 2020 at 09:23:32AM -0500, Justin Pryzby wrote: > On Wed, Jun 03, 2020 at 08:22:29PM +0800, 李杰(慎追) wrote: > > Partitioning is necessary for very large tables. > > However, I found that postgresql does not support create index concurrently on partitioned tables. > > The document show that we need to create an index on each partition individually and then finally create the partitioned index non-concurrently. > > This is undoubtedly a complex operation for DBA, especially when there are many partitions. > > > Therefore, I wonder why pg does not support concurrent index creation on partitioned tables? > > What are the difficulties of this function? > > If I want to implement it, what should I pay attention? > > Maybe I'm wrong, but I don't think there's any known difficulty - just that > nobody did it yet. I said that but I was actually thinking about the code for "REINDEX CONCURRENTLY" (which should also handle partitioned tables). I looked at CIC now and came up with the attached. All that's needed to allow this case is to close the relation before recursing to partitions - it needs to be closed before calling CommitTransactionCommand(). There's probably a better way to write this, but I can't see that there's anything complicated about handling partitioned tables. -- Justin --aVD9QWMuhilNxW9f Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v1-0001-Allow-CREATE-INDEX-CONCURRENTLY-on-partitioned-ta.patch" From de5f151805946b6999aa25aaddd9ebaaf91ccff2 Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Sat, 6 Jun 2020 17:42:23 -0500 Subject: [PATCH v1] Allow CREATE INDEX CONCURRENTLY on partitioned table --- doc/src/sgml/ref/create_index.sgml | 9 ----- src/backend/commands/indexcmds.c | 54 +++++++++++++++++------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index ff87b2d28f..e1298b8523 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -657,15 +657,6 @@ Indexes: cannot. - - Concurrent builds for indexes on partitioned tables are currently not - supported. However, you may concurrently build the index on each - partition individually and then finally create the partitioned index - non-concurrently in order to reduce the time where writes to the - partitioned table will be locked out. In this case, building the - partitioned index is a metadata only operation. - - diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 2baca12c5f..d2809c85a0 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -652,17 +652,6 @@ DefineIndex(Oid relationId, partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE; if (partitioned) { - /* - * Note: we check 'stmt->concurrent' rather than 'concurrent', so that - * the error is thrown also for temporary tables. Seems better to be - * consistent, even though we could do it on temporary table because - * we're not actually doing it concurrently. - */ - if (stmt->concurrent) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create index on partitioned table \"%s\" concurrently", - RelationGetRelationName(rel)))); if (stmt->excludeOpNames) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -1139,15 +1128,23 @@ DefineIndex(Oid relationId, CreateComments(indexRelationId, RelationRelationId, 0, stmt->idxcomment); + /* save lockrelid and locktag for below */ + heaprelid = rel->rd_lockInfo.lockRelId; + SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId); + if (partitioned) { + char *relname = pstrdup(RelationGetRelationName(rel)); + /* * Unless caller specified to skip this step (via ONLY), process each * partition to make sure they all contain a corresponding index. * * If we're called internally (no stmt->relation), recurse always. */ - if (!stmt->relation || stmt->relation->inh) + if (stmt->relation && !stmt->relation->inh) + table_close(rel, NoLock); + else { PartitionDesc partdesc = RelationGetPartitionDesc(rel); int nparts = partdesc->nparts; @@ -1166,6 +1163,8 @@ DefineIndex(Oid relationId, for (i = 0; i < numberOfKeyAttributes; i++) opfamOids[i] = get_opclass_family(classObjectId[i]); + table_close(rel, NoLock); + /* * For each partition, scan all existing indexes; if one matches * our index definition and is not already attached to some other @@ -1196,9 +1195,9 @@ DefineIndex(Oid relationId, ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot create unique index on partitioned table \"%s\"", - RelationGetRelationName(rel)), + relname), errdetail("Table \"%s\" contains partitions that are foreign tables.", - RelationGetRelationName(rel)))); + relname))); table_close(childrel, lockmode); continue; @@ -1365,21 +1364,35 @@ DefineIndex(Oid relationId, } } + /* + * CIC needs to mark a partitioned table as VALID, which itself + * requires setting READY, which is unset for CIC (even though + * it's meaningless for an index without storage). + */ + if (concurrent) + { + if (ActiveSnapshotSet()) + PopActiveSnapshot(); + CommitTransactionCommand(); + StartTransactionCommand(); + index_set_state_flags(indexRelationId, INDEX_CREATE_SET_READY); + + CommandCounterIncrement(); + index_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID); + } + /* * Indexes on partitioned tables are not themselves built, so we're * done here. */ - table_close(rel, NoLock); if (!OidIsValid(parentIndexId)) pgstat_progress_end_command(); return address; } + table_close(rel, NoLock); if (!concurrent) { - /* Close the heap and we're done, in the non-concurrent case */ - table_close(rel, NoLock); - /* If this is the top-level index, we're done. */ if (!OidIsValid(parentIndexId)) pgstat_progress_end_command(); @@ -1387,11 +1400,6 @@ DefineIndex(Oid relationId, return address; } - /* save lockrelid and locktag for below, then close rel */ - heaprelid = rel->rd_lockInfo.lockRelId; - SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId); - table_close(rel, NoLock); - /* * For a concurrent build, it's important to make the catalog entries * visible to other transactions before we start to build the index. That -- 2.17.0 --aVD9QWMuhilNxW9f--