Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3821292

Browse files
author
Commitfest Bot
committed
[CF 5467] v10 - speedup COPY TO for partitioned table
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5467 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CACJufxEp7btcpXiroNXcCvjwDwVT5-A6-A9ryWnDJiYfB7PO=Q@mail.gmail.com Author(s): Jian He
2 parents d98cefe + 4250d7b commit 3821292

File tree

4 files changed

+151
-32
lines changed

4 files changed

+151
-32
lines changed

doc/src/sgml/ref/copy.sgml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,13 @@ COPY <replaceable class="parameter">count</replaceable>
521521

522522
<para>
523523
<command>COPY TO</command> can be used with plain
524-
tables and populated materialized views.
525-
For example,
524+
tables, populated materialized views and partitioned tables.
525+
For example, if <replaceable class="parameter">table</replaceable> is not partitioned table,
526526
<literal>COPY <replaceable class="parameter">table</replaceable>
527527
TO</literal> copies the same rows as
528528
<literal>SELECT * FROM ONLY <replaceable class="parameter">table</replaceable></literal>.
529529
However it doesn't directly support other relation types,
530-
such as partitioned tables, inheritance child tables, or views.
530+
such as inheritance child tables, or views.
531531
To copy all rows from such relations, use <literal>COPY (SELECT * FROM
532532
<replaceable class="parameter">table</replaceable>) TO</literal>.
533533
</para>

src/backend/commands/copyto.c

Lines changed: 115 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include <sys/stat.h>
2020

2121
#include "access/tableam.h"
22+
#include "access/table.h"
23+
#include "catalog/pg_inherits.h"
2224
#include "commands/copyapi.h"
2325
#include "commands/progress.h"
2426
#include "executor/execdesc.h"
@@ -82,6 +84,7 @@ typedef struct CopyToStateData
8284
List *attnumlist; /* integer list of attnums to copy */
8385
char *filename; /* filename, or NULL for STDOUT */
8486
bool is_program; /* is 'filename' a program to popen? */
87+
List *partitions; /* oid list of partition oid for copy to */
8588
copy_data_dest_cb data_dest_cb; /* function for writing data */
8689

8790
CopyFormatOptions opts;
@@ -116,6 +119,8 @@ static void CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot);
116119
static void CopyAttributeOutText(CopyToState cstate, const char *string);
117120
static void CopyAttributeOutCSV(CopyToState cstate, const char *string,
118121
bool use_quote);
122+
static void CopyThisRelTo(CopyToState cstate, Relation rel,
123+
Relation root_rel, uint64 *processed);
119124

120125
/* built-in format-specific routines */
121126
static void CopyToTextLikeStart(CopyToState cstate, TupleDesc tupDesc);
@@ -643,6 +648,8 @@ BeginCopyTo(ParseState *pstate,
643648
PROGRESS_COPY_COMMAND_TO,
644649
0
645650
};
651+
List *children = NIL;
652+
List *scan_oids = NIL;
646653

647654
if (rel != NULL && rel->rd_rel->relkind != RELKIND_RELATION)
648655
{
@@ -673,11 +680,35 @@ BeginCopyTo(ParseState *pstate,
673680
errmsg("cannot copy from sequence \"%s\"",
674681
RelationGetRelationName(rel))));
675682
else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
676-
ereport(ERROR,
677-
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
678-
errmsg("cannot copy from partitioned table \"%s\"",
679-
RelationGetRelationName(rel)),
680-
errhint("Try the COPY (SELECT ...) TO variant.")));
683+
{
684+
children = find_all_inheritors(RelationGetRelid(rel),
685+
AccessShareLock,
686+
NULL);
687+
688+
foreach_oid(childreloid, children)
689+
{
690+
char relkind = get_rel_relkind(childreloid);
691+
692+
if (relkind == RELKIND_FOREIGN_TABLE)
693+
{
694+
char *relation_name;
695+
696+
relation_name = get_rel_name(childreloid);
697+
ereport(ERROR,
698+
errcode(ERRCODE_WRONG_OBJECT_TYPE),
699+
errmsg("cannot copy from foreign table \"%s\"", relation_name),
700+
errdetail("Partition \"%s\" is a foreign table in the partitioned table \"%s.%s\"",
701+
relation_name, RelationGetRelationName(rel),
702+
get_namespace_name(rel->rd_rel->relnamespace)),
703+
errhint("Try the COPY (SELECT ...) TO variant."));
704+
}
705+
706+
if (RELKIND_HAS_PARTITIONS(relkind))
707+
continue;
708+
709+
scan_oids = lappend_oid(scan_oids, childreloid);
710+
}
711+
}
681712
else
682713
ereport(ERROR,
683714
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -713,6 +744,7 @@ BeginCopyTo(ParseState *pstate,
713744
cstate->rel = rel;
714745

715746
tupDesc = RelationGetDescr(cstate->rel);
747+
cstate->partitions = list_copy(scan_oids);
716748
}
717749
else
718750
{
@@ -1030,7 +1062,7 @@ DoCopyTo(CopyToState cstate)
10301062
TupleDesc tupDesc;
10311063
int num_phys_attrs;
10321064
ListCell *cur;
1033-
uint64 processed;
1065+
uint64 processed = 0;
10341066

10351067
if (fe_copy)
10361068
SendCopyBegin(cstate);
@@ -1068,36 +1100,25 @@ DoCopyTo(CopyToState cstate)
10681100

10691101
cstate->routine->CopyToStart(cstate, tupDesc);
10701102

1071-
if (cstate->rel)
1103+
/*
1104+
* if COPY TO source table is a partitioned table, then open each
1105+
* partition and process each individual partition.
1106+
*/
1107+
if (cstate->rel && cstate->rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10721108
{
1073-
TupleTableSlot *slot;
1074-
TableScanDesc scandesc;
1075-
1076-
scandesc = table_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL);
1077-
slot = table_slot_create(cstate->rel, NULL);
1078-
1079-
processed = 0;
1080-
while (table_scan_getnextslot(scandesc, ForwardScanDirection, slot))
1109+
foreach_oid(scan_oid, cstate->partitions)
10811110
{
1082-
CHECK_FOR_INTERRUPTS();
1111+
Relation scan_rel;
10831112

1084-
/* Deconstruct the tuple ... */
1085-
slot_getallattrs(slot);
1113+
scan_rel = table_open(scan_oid, AccessShareLock);
10861114

1087-
/* Format and send the data */
1088-
CopyOneRowTo(cstate, slot);
1115+
CopyThisRelTo(cstate, scan_rel, cstate->rel, &processed);
10891116

1090-
/*
1091-
* Increment the number of processed tuples, and report the
1092-
* progress.
1093-
*/
1094-
pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED,
1095-
++processed);
1117+
table_close(scan_rel, AccessShareLock);
10961118
}
1097-
1098-
ExecDropSingleTupleTableSlot(slot);
1099-
table_endscan(scandesc);
11001119
}
1120+
else if (cstate->rel)
1121+
CopyThisRelTo(cstate, cstate->rel, NULL, &processed);
11011122
else
11021123
{
11031124
/* run the plan --- the dest receiver will send tuples */
@@ -1115,6 +1136,71 @@ DoCopyTo(CopyToState cstate)
11151136
return processed;
11161137
}
11171138

1139+
/*
1140+
* rel: the relation from which the actual data will be copied.
1141+
* root_rel: if not NULL, it indicates that we are copying partitioned relation
1142+
* data to the destination, and "rel" is the partition of "root_rel".
1143+
* processed: number of tuples processed.
1144+
*/
1145+
static void
1146+
CopyThisRelTo(CopyToState cstate, Relation rel, Relation root_rel,
1147+
uint64 *processed)
1148+
{
1149+
TupleTableSlot *slot;
1150+
TableScanDesc scandesc;
1151+
AttrMap *map = NULL;
1152+
TupleTableSlot *root_slot = NULL;
1153+
TupleTableSlot *original_slot = NULL;
1154+
TupleDesc scan_tupdesc;
1155+
TupleDesc rootdesc = NULL;
1156+
1157+
scan_tupdesc = RelationGetDescr(rel);
1158+
scandesc = table_beginscan(rel, GetActiveSnapshot(), 0, NULL);
1159+
slot = table_slot_create(rel, NULL);
1160+
1161+
/*
1162+
* A partition's rowtype might differ from the root table's.
1163+
* Since we are export partitioned table data here,
1164+
* we must convert it back to the root table's rowtype.
1165+
*/
1166+
if (root_rel != NULL)
1167+
{
1168+
rootdesc = RelationGetDescr(root_rel);
1169+
map = build_attrmap_by_name_if_req(rootdesc, scan_tupdesc, false);
1170+
}
1171+
1172+
while (table_scan_getnextslot(scandesc, ForwardScanDirection, slot))
1173+
{
1174+
CHECK_FOR_INTERRUPTS();
1175+
1176+
/* Deconstruct the tuple ... */
1177+
if (map != NULL)
1178+
{
1179+
original_slot = slot;
1180+
root_slot = MakeSingleTupleTableSlot(rootdesc, &TTSOpsBufferHeapTuple);
1181+
slot = execute_attr_map_slot(map, slot, root_slot);
1182+
}
1183+
else
1184+
slot_getallattrs(slot);
1185+
1186+
/* Format and send the data */
1187+
CopyOneRowTo(cstate, slot);
1188+
1189+
/*
1190+
* Increment the number of processed tuples, and report the
1191+
* progress.
1192+
*/
1193+
pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED,
1194+
++(*processed));
1195+
1196+
if (original_slot != NULL)
1197+
ExecDropSingleTupleTableSlot(original_slot);
1198+
}
1199+
1200+
ExecDropSingleTupleTableSlot(slot);
1201+
table_endscan(scandesc);
1202+
}
1203+
11181204
/*
11191205
* Emit one row during DoCopyTo().
11201206
*/

src/test/regress/expected/copy.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,21 @@ COPY copytest_mv(id) TO stdout WITH (header);
350350
id
351351
1
352352
DROP MATERIALIZED VIEW copytest_mv;
353+
-- Tests for COPY TO with partitioned tables.
354+
CREATE TABLE pp (id int,val int) PARTITION BY RANGE (id);
355+
CREATE TABLE pp_1 (val int, id int) PARTITION BY RANGE (id);
356+
CREATE TABLE pp_2 (val int, id int) PARTITION BY RANGE (id);
357+
ALTER TABLE pp ATTACH PARTITION pp_1 FOR VALUES FROM (1) TO (5);
358+
ALTER TABLE pp ATTACH PARTITION pp_2 FOR VALUES FROM (5) TO (10);
359+
CREATE TABLE pp_15 PARTITION OF pp_1 FOR VALUES FROM (1) TO (5);
360+
CREATE TABLE pp_510 PARTITION OF pp_2 FOR VALUES FROM (5) TO (10);
361+
INSERT INTO pp SELECT g, 10 + g FROM generate_series(1,6) g;
362+
COPY pp TO stdout(header);
363+
id val
364+
1 11
365+
2 12
366+
3 13
367+
4 14
368+
5 15
369+
6 16
370+
DROP TABLE PP;

src/test/regress/sql/copy.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,3 +375,18 @@ COPY copytest_mv(id) TO stdout WITH (header);
375375
REFRESH MATERIALIZED VIEW copytest_mv;
376376
COPY copytest_mv(id) TO stdout WITH (header);
377377
DROP MATERIALIZED VIEW copytest_mv;
378+
379+
-- Tests for COPY TO with partitioned tables.
380+
CREATE TABLE pp (id int,val int) PARTITION BY RANGE (id);
381+
CREATE TABLE pp_1 (val int, id int) PARTITION BY RANGE (id);
382+
CREATE TABLE pp_2 (val int, id int) PARTITION BY RANGE (id);
383+
ALTER TABLE pp ATTACH PARTITION pp_1 FOR VALUES FROM (1) TO (5);
384+
ALTER TABLE pp ATTACH PARTITION pp_2 FOR VALUES FROM (5) TO (10);
385+
386+
CREATE TABLE pp_15 PARTITION OF pp_1 FOR VALUES FROM (1) TO (5);
387+
CREATE TABLE pp_510 PARTITION OF pp_2 FOR VALUES FROM (5) TO (10);
388+
389+
INSERT INTO pp SELECT g, 10 + g FROM generate_series(1,6) g;
390+
391+
COPY pp TO stdout(header);
392+
DROP TABLE PP;

0 commit comments

Comments
 (0)