19
19
#include <sys/stat.h>
20
20
21
21
#include "access/tableam.h"
22
+ #include "access/table.h"
23
+ #include "catalog/pg_inherits.h"
22
24
#include "commands/copyapi.h"
23
25
#include "commands/progress.h"
24
26
#include "executor/execdesc.h"
@@ -82,6 +84,7 @@ typedef struct CopyToStateData
82
84
List * attnumlist ; /* integer list of attnums to copy */
83
85
char * filename ; /* filename, or NULL for STDOUT */
84
86
bool is_program ; /* is 'filename' a program to popen? */
87
+ List * partitions ; /* oid list of partition oid for copy to */
85
88
copy_data_dest_cb data_dest_cb ; /* function for writing data */
86
89
87
90
CopyFormatOptions opts ;
@@ -116,6 +119,8 @@ static void CopyOneRowTo(CopyToState cstate, TupleTableSlot *slot);
116
119
static void CopyAttributeOutText (CopyToState cstate , const char * string );
117
120
static void CopyAttributeOutCSV (CopyToState cstate , const char * string ,
118
121
bool use_quote );
122
+ static void CopyThisRelTo (CopyToState cstate , Relation rel ,
123
+ Relation root_rel , uint64 * processed );
119
124
120
125
/* built-in format-specific routines */
121
126
static void CopyToTextLikeStart (CopyToState cstate , TupleDesc tupDesc );
@@ -643,6 +648,8 @@ BeginCopyTo(ParseState *pstate,
643
648
PROGRESS_COPY_COMMAND_TO ,
644
649
0
645
650
};
651
+ List * children = NIL ;
652
+ List * scan_oids = NIL ;
646
653
647
654
if (rel != NULL && rel -> rd_rel -> relkind != RELKIND_RELATION )
648
655
{
@@ -673,11 +680,35 @@ BeginCopyTo(ParseState *pstate,
673
680
errmsg ("cannot copy from sequence \"%s\"" ,
674
681
RelationGetRelationName (rel ))));
675
682
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
+ }
681
712
else
682
713
ereport (ERROR ,
683
714
(errcode (ERRCODE_WRONG_OBJECT_TYPE ),
@@ -713,6 +744,7 @@ BeginCopyTo(ParseState *pstate,
713
744
cstate -> rel = rel ;
714
745
715
746
tupDesc = RelationGetDescr (cstate -> rel );
747
+ cstate -> partitions = list_copy (scan_oids );
716
748
}
717
749
else
718
750
{
@@ -1030,7 +1062,7 @@ DoCopyTo(CopyToState cstate)
1030
1062
TupleDesc tupDesc ;
1031
1063
int num_phys_attrs ;
1032
1064
ListCell * cur ;
1033
- uint64 processed ;
1065
+ uint64 processed = 0 ;
1034
1066
1035
1067
if (fe_copy )
1036
1068
SendCopyBegin (cstate );
@@ -1068,36 +1100,25 @@ DoCopyTo(CopyToState cstate)
1068
1100
1069
1101
cstate -> routine -> CopyToStart (cstate , tupDesc );
1070
1102
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 )
1072
1108
{
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 )
1081
1110
{
1082
- CHECK_FOR_INTERRUPTS () ;
1111
+ Relation scan_rel ;
1083
1112
1084
- /* Deconstruct the tuple ... */
1085
- slot_getallattrs (slot );
1113
+ scan_rel = table_open (scan_oid , AccessShareLock );
1086
1114
1087
- /* Format and send the data */
1088
- CopyOneRowTo (cstate , slot );
1115
+ CopyThisRelTo (cstate , scan_rel , cstate -> rel , & processed );
1089
1116
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 );
1096
1118
}
1097
-
1098
- ExecDropSingleTupleTableSlot (slot );
1099
- table_endscan (scandesc );
1100
1119
}
1120
+ else if (cstate -> rel )
1121
+ CopyThisRelTo (cstate , cstate -> rel , NULL , & processed );
1101
1122
else
1102
1123
{
1103
1124
/* run the plan --- the dest receiver will send tuples */
@@ -1115,6 +1136,71 @@ DoCopyTo(CopyToState cstate)
1115
1136
return processed ;
1116
1137
}
1117
1138
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
+
1118
1204
/*
1119
1205
* Emit one row during DoCopyTo().
1120
1206
*/
0 commit comments