@@ -787,23 +787,27 @@ copy_read_data(void *outbuf, int minread, int maxread)
787787
788788/*
789789 * Get information about remote relation in similar fashion the RELATION
790- * message provides during replication. This function also returns the relation
791- * qualifications to be used in the COPY command.
790+ * message provides during replication.
791+ *
792+ * This function also returns (a) the relation qualifications to be used in
793+ * the COPY command, and (b) whether the remote relation has published any
794+ * generated column.
792795 */
793796static void
794- fetch_remote_table_info (char * nspname , char * relname ,
795- LogicalRepRelation * lrel , List * * qual )
797+ fetch_remote_table_info (char * nspname , char * relname , LogicalRepRelation * lrel ,
798+ List * * qual , bool * gencol_published )
796799{
797800 WalRcvExecResult * res ;
798801 StringInfoData cmd ;
799802 TupleTableSlot * slot ;
800803 Oid tableRow [] = {OIDOID , CHAROID , CHAROID };
801- Oid attrRow [] = {INT2OID , TEXTOID , OIDOID , BOOLOID };
804+ Oid attrRow [] = {INT2OID , TEXTOID , OIDOID , BOOLOID , BOOLOID };
802805 Oid qualRow [] = {TEXTOID };
803806 bool isnull ;
804807 int natt ;
805808 StringInfo pub_names = NULL ;
806809 Bitmapset * included_cols = NULL ;
810+ int server_version = walrcv_server_version (LogRepWorkerWalRcvConn );
807811
808812 lrel -> nspname = nspname ;
809813 lrel -> relname = relname ;
@@ -851,7 +855,7 @@ fetch_remote_table_info(char *nspname, char *relname,
851855 * We need to do this before fetching info about column names and types,
852856 * so that we can skip columns that should not be replicated.
853857 */
854- if (walrcv_server_version ( LogRepWorkerWalRcvConn ) >= 150000 )
858+ if (server_version >= 150000 )
855859 {
856860 WalRcvExecResult * pubres ;
857861 TupleTableSlot * tslot ;
@@ -941,7 +945,13 @@ fetch_remote_table_info(char *nspname, char *relname,
941945 "SELECT a.attnum,"
942946 " a.attname,"
943947 " a.atttypid,"
944- " a.attnum = ANY(i.indkey)"
948+ " a.attnum = ANY(i.indkey)" );
949+
950+ /* Generated columns can be replicated since version 18. */
951+ if (server_version >= 180000 )
952+ appendStringInfo (& cmd , ", a.attgenerated != ''" );
953+
954+ appendStringInfo (& cmd ,
945955 " FROM pg_catalog.pg_attribute a"
946956 " LEFT JOIN pg_catalog.pg_index i"
947957 " ON (i.indexrelid = pg_get_replica_identity_index(%u))"
@@ -950,11 +960,11 @@ fetch_remote_table_info(char *nspname, char *relname,
950960 " AND a.attrelid = %u"
951961 " ORDER BY a.attnum" ,
952962 lrel -> remoteid ,
953- (walrcv_server_version ( LogRepWorkerWalRcvConn ) >= 120000 ?
963+ (server_version >= 120000 && server_version < 180000 ?
954964 "AND a.attgenerated = ''" : "" ),
955965 lrel -> remoteid );
956966 res = walrcv_exec (LogRepWorkerWalRcvConn , cmd .data ,
957- lengthof (attrRow ), attrRow );
967+ server_version >= 180000 ? lengthof (attrRow ) : lengthof ( attrRow ) - 1 , attrRow );
958968
959969 if (res -> status != WALRCV_OK_TUPLES )
960970 ereport (ERROR ,
@@ -998,6 +1008,13 @@ fetch_remote_table_info(char *nspname, char *relname,
9981008 if (DatumGetBool (slot_getattr (slot , 4 , & isnull )))
9991009 lrel -> attkeys = bms_add_member (lrel -> attkeys , natt );
10001010
1011+ /* Remember if the remote table has published any generated column. */
1012+ if (server_version >= 180000 && !(* gencol_published ))
1013+ {
1014+ * gencol_published = DatumGetBool (slot_getattr (slot , 5 , & isnull ));
1015+ Assert (!isnull );
1016+ }
1017+
10011018 /* Should never happen. */
10021019 if (++ natt >= MaxTupleAttributeNumber )
10031020 elog (ERROR , "too many columns in remote table \"%s.%s\"" ,
@@ -1030,7 +1047,7 @@ fetch_remote_table_info(char *nspname, char *relname,
10301047 * 3) one of the subscribed publications is declared as TABLES IN SCHEMA
10311048 * that includes this relation
10321049 */
1033- if (walrcv_server_version ( LogRepWorkerWalRcvConn ) >= 150000 )
1050+ if (server_version >= 150000 )
10341051 {
10351052 /* Reuse the already-built pub_names. */
10361053 Assert (pub_names != NULL );
@@ -1106,10 +1123,12 @@ copy_table(Relation rel)
11061123 List * attnamelist ;
11071124 ParseState * pstate ;
11081125 List * options = NIL ;
1126+ bool gencol_published = false;
11091127
11101128 /* Get the publisher relation info. */
11111129 fetch_remote_table_info (get_namespace_name (RelationGetNamespace (rel )),
1112- RelationGetRelationName (rel ), & lrel , & qual );
1130+ RelationGetRelationName (rel ), & lrel , & qual ,
1131+ & gencol_published );
11131132
11141133 /* Put the relation into relmap. */
11151134 logicalrep_relmap_update (& lrel );
@@ -1121,8 +1140,8 @@ copy_table(Relation rel)
11211140 /* Start copy on the publisher. */
11221141 initStringInfo (& cmd );
11231142
1124- /* Regular table with no row filter */
1125- if (lrel .relkind == RELKIND_RELATION && qual == NIL )
1143+ /* Regular table with no row filter or generated columns */
1144+ if (lrel .relkind == RELKIND_RELATION && qual == NIL && ! gencol_published )
11261145 {
11271146 appendStringInfo (& cmd , "COPY %s" ,
11281147 quote_qualified_identifier (lrel .nspname , lrel .relname ));
@@ -1153,9 +1172,14 @@ copy_table(Relation rel)
11531172 {
11541173 /*
11551174 * For non-tables and tables with row filters, we need to do COPY
1156- * (SELECT ...), but we can't just do SELECT * because we need to not
1157- * copy generated columns. For tables with any row filters, build a
1158- * SELECT query with OR'ed row filters for COPY.
1175+ * (SELECT ...), but we can't just do SELECT * because we may need to
1176+ * copy only subset of columns including generated columns. For tables
1177+ * with any row filters, build a SELECT query with OR'ed row filters
1178+ * for COPY.
1179+ *
1180+ * We also need to use this same COPY (SELECT ...) syntax when
1181+ * generated columns are published, because copy of generated columns
1182+ * is not supported by the normal COPY.
11591183 */
11601184 appendStringInfoString (& cmd , "COPY (SELECT " );
11611185 for (int i = 0 ; i < lrel .natts ; i ++ )
0 commit comments