Disallow COPY FREEZE on partitioned tables
authorAlvaro Herrera <[email protected]>
Mon, 19 Nov 2018 14:16:28 +0000 (11:16 -0300)
committerAlvaro Herrera <[email protected]>
Mon, 19 Nov 2018 14:16:28 +0000 (11:16 -0300)
This didn't actually work: COPY would fail to flush the right files, and
instead would try to flush a non-existing file, causing the whole
transaction to fail.

Cope by raising an error as soon as the command is sent instead, to
avoid a nasty later surprise.  Of course, it would be much better to
make it work, but we don't have a  for that yet, and we don't know
if we'll want to back one when we do.

Reported-by: Tomas Vondra
Author: David Rowley
Reviewed-by: Amit Langote, Steve Singer, Tomas Vondra
doc/src/sgml/perform.sgml
doc/src/sgml/ref/copy.sgml
src/backend/commands/copy.c
src/test/regress/input/copy.source
src/test/regress/output/copy.source

index 262f30fd4c7bff53c9cee4063966356da7d42e83..7373af860cf7a83223c9c1cbc3837a5acc5948a2 100644 (file)
@@ -1535,8 +1535,8 @@ SELECT * FROM x, y, a, b, c WHERE something AND somethingelse;
     needs to be written, because in case of an error, the files
     containing the newly loaded data will be removed anyway.
     However, this consideration only applies when
-    <xref linkend="guc-wal-level"/> is <literal>minimal</literal> as all commands
-    must write WAL otherwise.
+    <xref linkend="guc-wal-level"/> is <literal>minimal</literal> for
+    non-partitioned tables as all commands must write WAL otherwise.
    </para>
 
   </sect2>
index 13a8b68d9511ed044e066e27825323626867f835..9f3c85bf7f9f649d98833d0f7ae7a49129adf657 100644 (file)
@@ -224,7 +224,9 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
       This is intended as a performance option for initial data loading.
       Rows will be frozen only if the table being loaded has been created
       or truncated in the current subtransaction, there are no cursors
-      open and there are no older snapshots held by this transaction.
+      open and there are no older snapshots held by this transaction.  It is
+      currently not possible to perform a <command>COPY FREEZE</command> on
+      a partitioned table.
      </para>
      <para>
       Note that all other sessions will immediately be able to see the data
index a61f3bca4fb626ac2d9d0cddb8acaccc92bfa6fb..7e5249e1e20399dd79470af4c706fa47de02a7b8 100644 (file)
@@ -2416,11 +2416,20 @@ CopyFrom(CopyState cstate)
     * go into pages containing tuples from any other transactions --- but this
     * must be the case if we have a new table or new relfilenode, so we need
     * no additional work to enforce that.
+    *
+    * We currently don't support this optimization if the COPY target is a
+    * partitioned table as we currently only lazily initialize partition
+    * information when routing the first tuple to the partition.  We cannot
+    * know at this stage if we can perform this optimization.  It should be
+    * possible to improve on this, but it does mean maintaining heap insert
+    * option flags per partition and setting them when we first open the
+    * partition.
     *----------
     */
    /* createSubid is creation check, newRelfilenodeSubid is truncation check */
-   if (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
-       cstate->rel->rd_newRelfilenodeSubid != InvalidSubTransactionId)
+   if (cstate->rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
+       (cstate->rel->rd_createSubid != InvalidSubTransactionId ||
+        cstate->rel->rd_newRelfilenodeSubid != InvalidSubTransactionId))
    {
        hi_options |= HEAP_INSERT_SKIP_FSM;
        if (!XLogIsNeeded())
@@ -2440,6 +2449,22 @@ CopyFrom(CopyState cstate)
     */
    if (cstate->freeze)
    {
+       /*
+        * We currently disallow COPY FREEZE on partitioned tables.  The
+        * reason for this is that we've simply not yet opened the partitions
+        * to determine if the optimization can be applied to them.  We could
+        * go and open them all here, but doing so may be quite a costly
+        * overhead for small copies.  In any case, we may just end up routing
+        * tuples to a small number of partitions.  It seems better just to
+        * raise an ERROR for partitioned tables.
+        */
+       if (cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+       {
+           ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                   errmsg("cannot perform FREEZE on a partitioned table")));
+       }
+
        /*
         * Tolerate one registration for the benefit of FirstXactSnapshot.
         * Scan-bearing queries generally create at least two registrations,
index 4cb03c566fabc73173e66a639113142458c6cdb9..aefc99167afeaa42bff30658d236243df194a20c 100644 (file)
@@ -159,6 +159,12 @@ truncate parted_copytest;
 
 copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
 
+-- Ensure COPY FREEZE errors for partitioned tables.
+begin;
+truncate parted_copytest;
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' (freeze);
+rollback;
+
 select tableoid::regclass,count(*),sum(a) from parted_copytest
 group by tableoid order by tableoid::regclass::name;
 
index ddd652c7128fe6d52332d7db4e42dc95ff5e2fa9..b16247230a019374bb69d0cd12cb5aeb4750de83 100644 (file)
@@ -113,6 +113,12 @@ insert into parted_copytest select x,1,'One' from generate_series(1011,1020) x;
 copy (select * from parted_copytest order by a) to '@abs_builddir@/results/parted_copytest.csv';
 truncate parted_copytest;
 copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv';
+-- Ensure COPY FREEZE errors for partitioned tables.
+begin;
+truncate parted_copytest;
+copy parted_copytest from '@abs_builddir@/results/parted_copytest.csv' (freeze);
+ERROR:  cannot perform FREEZE on a partitioned table
+rollback;
 select tableoid::regclass,count(*),sum(a) from parted_copytest
 group by tableoid order by tableoid::regclass::name;
       tableoid      | count |  sum