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.89) (envelope-from ) id 1hDzKg-0007xP-QQ for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Apr 2019 22:35:10 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1hDzKf-0001tZ-Br for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Apr 2019 22:35:09 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1hDzKc-0001rR-Id for pgsql-hackers@lists.postgresql.org; Tue, 09 Apr 2019 22:35:09 +0000 Received: from mail-qk1-x741.google.com ([2607:f8b0:4864:20::741]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1hDzKC-0007nG-EA for pgsql-hackers@lists.postgresql.org; Tue, 09 Apr 2019 22:34:42 +0000 Received: by mail-qk1-x741.google.com with SMTP id s81so25316qke.13 for ; Tue, 09 Apr 2019 15:34:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :content-transfer-encoding:in-reply-to:user-agent; bh=aRPb82BvcUjnSSt1Rog8mmamtu0b5qwdJDQaGb4xIZo=; b=tADOnTzqOS730j91OjMgbC6ypEDdZCxNKnh85RgcwHfNaxFfHTKtZVnnTzJK91wHVX Xm8hMl7MDNHO6Cdw0SM50k0qFVJ9moX3AkMkAgFJChZHWEuNjfh1R17okmnWMbcwheBJ erai1o+TciQvZR1IarCr0Uy3bLJs8rZ15vfHgaWBypL0XQpgihYCuowa7gr0vfG9jaOj EUH0CUwz59WF1EnwPPQKPyeyuCRElDKB11VQcdt8b0qTLaXWZ4HTeewfH2Rdt/AOwo/1 JBi7zo57R6B6y+iJElh0YrBF5gIuYhGwvbZ6DM2/tH03r/e6yxxSQavX4bxOKKxW0D2d i9Zg== 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:mime-version :content-disposition:content-transfer-encoding:in-reply-to :user-agent; bh=aRPb82BvcUjnSSt1Rog8mmamtu0b5qwdJDQaGb4xIZo=; b=AfeA+3ELVyocntMdhXhGezxDrbsvbvI8RTaGwt5E6xD5HDrnufcI/sDsdDrpsTy2g/ 6dl8FET9wQH9UhElQCHQnKj0KcbLKCucV16tuQbNAvBq3QTTP/ONVWpvWkoL8bTVzp7i C1Uwysf3oN5L24E63upXTtlP7EAg4vQECrMG9bP4pa+CxpyEWFE+BIf8E4I4j7jhejVX cwbhzzN6G8l1eQQTEb0C1gNGpOJbIR/3l30UdBfFR10GYEimpZUhWOzhJaC6abI2uoSB KFBF+r/XOlABmqt+QbySei8+13VMtjDtHXLDI/ndr9jYUifkQtUbFfA9lq5IFihPKcQ4 Bcqg== X-Gm-Message-State: APjAAAWZtix5oIsadVN9lKk+U7bw/dC/rVSU2A6r9B0HBu9j9kemyFt/ EZwvABGn7pALa8RG3JkvY4sGpw== X-Google-Smtp-Source: APXvYqyPxp9HJxOeUExnYVk+UF3NmN3XPC3EWrfmeNXoCnYa69pAqYamDY4XHikUGRxiyU/puLnsVQ== X-Received: by 2002:a37:553:: with SMTP id 80mr31101464qkf.243.1554849278953; Tue, 09 Apr 2019 15:34:38 -0700 (PDT) Received: from nimloth.alvh.no-ip.org ([190.121.29.3]) by smtp.gmail.com with ESMTPSA id a20sm19304489qth.88.2019.04.09.15.34.37 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Tue, 09 Apr 2019 15:34:37 -0700 (PDT) Received: by nimloth.alvh.no-ip.org (Postfix, from userid 1000) id 800B61214F4; Tue, 9 Apr 2019 18:34:35 -0400 (-04) Date: Tue, 9 Apr 2019 18:34:35 -0400 From: Alvaro Herrera To: David Rowley Cc: Andres Freund , PostgreSQL Hackers Subject: Re: pg_dump is broken for partition tablespaces Message-ID: <20190409223435.GA14335@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="YiEDa0DAkWCtVeE4" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20190409203507.GA25150@alvherre.pgsql> User-Agent: Mutt/1.9.4 (2018-02-28) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --YiEDa0DAkWCtVeE4 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2019-Apr-09, Alvaro Herrera wrote: > However, in order to fix the pg_dump behavior for the partitioned rel, > we would need to emit the tablespace differently, i.e. not use SET > default_tablespace, but instead attach the tablespace clause to the > CREATE TABLE line. > > I'll go have a look at what problems are there with doing that. I tried with the attached tbspc-partitioned.patch (on top of the previous patch). It makes several additional cases work correctly, but causes much more pain than it fixes: 1. if a partitioned table is created without a tablespace spec, then pg_dump dumps it with a clause like TABLESPACE "". 2. If you fix that to make pg_dump omit the tablespace clause if the tablespace is default, then there's no SET nor TABLESPACE clauses, so the table is created in the tablespace of the previously dumped table. Useless. 3. You can probably make the above work anyway with enough hacks, but by the time you're finished, the code stinks like months-only fish. 4. Even if you ignore the odor, all the resulting CREATE TABLE commands will fail if you run them in a database that doesn't contain the tablespaces in question. That is, it negates one of the main points of using "SET default_tablespace" in the first place. I therefore decree that this approach is not a solution and never will be. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --YiEDa0DAkWCtVeE4 Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="tbspc-partitioned.patch" diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 6b77eff0af1..3a778ce5c05 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -386,9 +386,19 @@ heap_create(const char *relname, * tablespace, the pg_class entry will still match where CREATE DATABASE * will put the physically copied relation. * - * Yes, this is a bit of a hack. + * We make an exception for partitioned relations, though: for them, + * reltablespace does not really indicate a storage location (there isn't + * any storage in the first place) but instead it only determines where + * storage for partitions is to be created later. We must make this + * specific even with the default tablespace, so that partitions are + * created in the right location even if the database's default tablespace + * is later changed. + * + * Yes, this is all a bit of a hack. */ - if (reltablespace == MyDatabaseTableSpace) + if (reltablespace == MyDatabaseTableSpace && + relkind != RELKIND_PARTITIONED_TABLE && + relkind != RELKIND_PARTITIONED_INDEX) reltablespace = InvalidOid; /* diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a7987790df5..e15e92c1a2a 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15785,6 +15785,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) if (ftoptions && ftoptions[0]) appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions); + /* + * For partitioned tables, emit the tablespace spec (if there is one). + */ + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE && + tbinfo->reltablespace) + appendPQExpBuffer(q, "\nTABLESPACE %s", + fmtId(tbinfo->reltablespace)); + /* * For materialized views, create the AS clause just like a view. At * this point, we always mark the view as not populated. @@ -16166,7 +16174,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, ARCHIVE_OPTS(.tag = tbinfo->dobj.name, .namespace = tbinfo->dobj.namespace->dobj.name, - .tablespace = (tbinfo->relkind == RELKIND_VIEW) ? + .tablespace = (tbinfo->relkind == RELKIND_VIEW || + tbinfo->relkind == RELKIND_PARTITIONED_TABLE) ? NULL : tbinfo->reltablespace, .tableam = tableam, .owner = tbinfo->rolname, @@ -16334,7 +16343,11 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) indxinfo->dobj.catId.oid, true); /* Plain secondary index */ - appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef); + appendPQExpBuffer(q, "%s\n", indxinfo->indexdef); + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE && + indxinfo->tablespace) + appendPQExpBuffer(q, "TABLESPACE %s", indxinfo->tablespace); + appendPQExpBufferStr(q, ";"); /* * Append ALTER TABLE commands as needed to set properties that we @@ -16395,7 +16408,8 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId, ARCHIVE_OPTS(.tag = indxinfo->dobj.name, .namespace = tbinfo->dobj.namespace->dobj.name, - .tablespace = indxinfo->tablespace, + .tablespace = (tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? + NULL : indxinfo->tablespace), .owner = tbinfo->rolname, .description = "INDEX", .section = SECTION_POST_DATA, @@ -16618,6 +16632,11 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) appendPQExpBufferStr(q, " INITIALLY DEFERRED"); } + if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE && + indxinfo->tablespace) + appendPQExpBuffer(q, "USING INDEX TABLESPACE %s", + indxinfo->tablespace); + appendPQExpBufferStr(q, ";\n"); } @@ -16658,7 +16677,8 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId, ARCHIVE_OPTS(.tag = tag, .namespace = tbinfo->dobj.namespace->dobj.name, - .tablespace = indxinfo->tablespace, + .tablespace = (tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? + NULL : indxinfo->tablespace), .owner = tbinfo->rolname, .description = "CONSTRAINT", .section = SECTION_POST_DATA, --YiEDa0DAkWCtVeE4--