1818#include <unistd.h>
1919#include <sys/stat.h>
2020
21+ #include "access/table.h"
2122#include "access/tableam.h"
23+ #include "catalog/pg_inherits.h"
2224#include "commands/copyapi.h"
2325#include "commands/progress.h"
2426#include "executor/execdesc.h"
@@ -86,6 +88,7 @@ typedef struct CopyToStateData
8688
8789 CopyFormatOptions opts ;
8890 Node * whereClause ; /* WHERE condition (or NULL) */
91+ List * partitions ; /* OID list of partitions to copy data from */
8992
9093 /*
9194 * Working state
@@ -116,6 +119,8 @@ static void CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot);
116119static void CopyAttributeOutText (CopyToState cstate , const char * string );
117120static void CopyAttributeOutCSV (CopyToState cstate , const char * string ,
118121 bool use_quote );
122+ static void CopyRelationTo (CopyToState cstate , Relation rel , Relation root_rel ,
123+ uint64 * processed );
119124
120125/* built-in format-specific routines */
121126static void CopyToTextLikeStart (CopyToState cstate , TupleDesc tupDesc );
@@ -602,6 +607,10 @@ EndCopy(CopyToState cstate)
602607 pgstat_progress_end_command ();
603608
604609 MemoryContextDelete (cstate -> copycontext );
610+
611+ if (cstate -> partitions )
612+ list_free (cstate -> partitions );
613+
605614 pfree (cstate );
606615}
607616
@@ -643,6 +652,7 @@ BeginCopyTo(ParseState *pstate,
643652 PROGRESS_COPY_COMMAND_TO ,
644653 0
645654 };
655+ List * children = NIL ;
646656
647657 if (rel != NULL && rel -> rd_rel -> relkind != RELKIND_RELATION )
648658 {
@@ -673,11 +683,34 @@ BeginCopyTo(ParseState *pstate,
673683 errmsg ("cannot copy from sequence \"%s\"" ,
674684 RelationGetRelationName (rel ))));
675685 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." )));
686+ {
687+ /*
688+ * Collect OIDs of relation containing data, so that later
689+ * DoCopyTo can copy the data from them.
690+ */
691+ children = find_all_inheritors (RelationGetRelid (rel ), AccessShareLock , NULL );
692+
693+ foreach_oid (child , children )
694+ {
695+ char relkind = get_rel_relkind (child );
696+
697+ if (relkind == RELKIND_FOREIGN_TABLE )
698+ {
699+ char * relation_name = get_rel_name (child );
700+
701+ ereport (ERROR ,
702+ errcode (ERRCODE_WRONG_OBJECT_TYPE ),
703+ errmsg ("cannot copy from foreign table \"%s\"" , relation_name ),
704+ errdetail ("Partition \"%s\" is a foreign table in partitioned table \"%s\"" ,
705+ relation_name , RelationGetRelationName (rel )),
706+ errhint ("Try the COPY (SELECT ...) TO variant." ));
707+ }
708+
709+ /* Exclude tables with no data */
710+ if (RELKIND_HAS_PARTITIONS (relkind ))
711+ children = foreach_delete_current (children , child );
712+ }
713+ }
681714 else
682715 ereport (ERROR ,
683716 (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
@@ -713,6 +746,7 @@ BeginCopyTo(ParseState *pstate,
713746 cstate -> rel = rel ;
714747
715748 tupDesc = RelationGetDescr (cstate -> rel );
749+ cstate -> partitions = children ;
716750 }
717751 else
718752 {
@@ -722,6 +756,7 @@ BeginCopyTo(ParseState *pstate,
722756 DestReceiver * dest ;
723757
724758 cstate -> rel = NULL ;
759+ cstate -> partitions = NIL ;
725760
726761 /*
727762 * Run parse analysis and rewrite. Note this also acquires sufficient
@@ -1030,7 +1065,7 @@ DoCopyTo(CopyToState cstate)
10301065 TupleDesc tupDesc ;
10311066 int num_phys_attrs ;
10321067 ListCell * cur ;
1033- uint64 processed ;
1068+ uint64 processed = 0 ;
10341069
10351070 if (fe_copy )
10361071 SendCopyBegin (cstate );
@@ -1070,33 +1105,24 @@ DoCopyTo(CopyToState cstate)
10701105
10711106 if (cstate -> rel )
10721107 {
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 ))
1108+ /*
1109+ * If COPY TO source table is a partitioned table, then open each
1110+ * partition and process each individual partition.
1111+ */
1112+ if (cstate -> rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
10811113 {
1082- CHECK_FOR_INTERRUPTS ();
1083-
1084- /* Deconstruct the tuple ... */
1085- slot_getallattrs (slot );
1086-
1087- /* Format and send the data */
1088- CopyOneRowTo (cstate , slot );
1114+ foreach_oid (child , cstate -> partitions )
1115+ {
1116+ Relation scan_rel ;
10891117
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 );
1118+ /* We already got the lock in BeginCopyTo */
1119+ scan_rel = table_open (child , NoLock );
1120+ CopyRelationTo (cstate , scan_rel , cstate -> rel , & processed );
1121+ table_close (scan_rel , NoLock );
1122+ }
10961123 }
1097-
1098- ExecDropSingleTupleTableSlot (slot );
1099- table_endscan (scandesc );
1124+ else
1125+ CopyRelationTo (cstate , cstate -> rel , NULL , & processed );
11001126 }
11011127 else
11021128 {
@@ -1115,6 +1141,73 @@ DoCopyTo(CopyToState cstate)
11151141 return processed ;
11161142}
11171143
1144+ /*
1145+ * Scans a single table and exports its rows to the COPY destination.
1146+ *
1147+ * root_rel can be set to the root table of rel if rel is a partition
1148+ * table so that we can send tuples in root_rel's rowtype, which might
1149+ * differ from individual partitions.
1150+ */
1151+ static void
1152+ CopyRelationTo (CopyToState cstate , Relation rel , Relation root_rel , uint64 * processed )
1153+ {
1154+ TupleTableSlot * slot ;
1155+ TableScanDesc scandesc ;
1156+ AttrMap * map = NULL ;
1157+ TupleTableSlot * root_slot = NULL ;
1158+
1159+ scandesc = table_beginscan (rel , GetActiveSnapshot (), 0 , NULL );
1160+ slot = table_slot_create (rel , NULL );
1161+
1162+ /*
1163+ * If we are exporting partition data here, we check if converting tuples
1164+ * to the root table's rowtype, because a partition might have column
1165+ * order different than its root table.
1166+ */
1167+ if (root_rel != NULL )
1168+ {
1169+ root_slot = table_slot_create (root_rel , NULL );
1170+ map = build_attrmap_by_name_if_req (RelationGetDescr (root_rel ),
1171+ RelationGetDescr (rel ),
1172+ false);
1173+ }
1174+
1175+ while (table_scan_getnextslot (scandesc , ForwardScanDirection , slot ))
1176+ {
1177+ TupleTableSlot * copyslot ;
1178+
1179+ CHECK_FOR_INTERRUPTS ();
1180+
1181+ if (map != NULL )
1182+ copyslot = execute_attr_map_slot (map , slot , root_slot );
1183+ else
1184+ {
1185+ /* Deconstruct the tuple */
1186+ slot_getallattrs (slot );
1187+ copyslot = slot ;
1188+ }
1189+
1190+ /* Format and send the data */
1191+ CopyOneRowTo (cstate , copyslot );
1192+
1193+ /*
1194+ * Increment the number of processed tuples, and report the progress.
1195+ */
1196+ pgstat_progress_update_param (PROGRESS_COPY_TUPLES_PROCESSED ,
1197+ ++ (* processed ));
1198+ }
1199+
1200+ ExecDropSingleTupleTableSlot (slot );
1201+
1202+ if (root_slot != NULL )
1203+ ExecDropSingleTupleTableSlot (root_slot );
1204+
1205+ if (map != NULL )
1206+ free_attrmap (map );
1207+
1208+ table_endscan (scandesc );
1209+ }
1210+
11181211/*
11191212 * Emit one row during DoCopyTo().
11201213 */
0 commit comments