@@ -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{
797800WalRcvExecResult * res ;
798801StringInfoData cmd ;
799802TupleTableSlot * slot ;
800803Oid tableRow []= {OIDOID ,CHAROID ,CHAROID };
801- Oid attrRow []= {INT2OID ,TEXTOID ,OIDOID ,BOOLOID };
804+ Oid attrRow []= {INT2OID ,TEXTOID ,OIDOID ,BOOLOID , BOOLOID };
802805Oid qualRow []= {TEXTOID };
803806bool isnull ;
804807int natt ;
805808StringInfo pub_names = NULL ;
806809Bitmapset * included_cols = NULL ;
810+ int server_version = walrcv_server_version (LogRepWorkerWalRcvConn );
807811
808812lrel -> nspname = nspname ;
809813lrel -> 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{
856860WalRcvExecResult * pubres ;
857861TupleTableSlot * 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" ,
952962lrel -> remoteid ,
953- (walrcv_server_version ( LogRepWorkerWalRcvConn ) >=120000 ?
963+ (server_version >=120000 && server_version < 180000 ?
954964"AND a.attgenerated = ''" :"" ),
955965lrel -> remoteid );
956966res = walrcv_exec (LogRepWorkerWalRcvConn ,cmd .data ,
957- lengthof (attrRow ),attrRow );
967+ server_version >= 180000 ? lengthof (attrRow ) : lengthof ( attrRow ) - 1 ,attrRow );
958968
959969if (res -> status != WALRCV_OK_TUPLES )
960970ereport (ERROR ,
@@ -998,6 +1008,13 @@ fetch_remote_table_info(char *nspname, char *relname,
9981008if (DatumGetBool (slot_getattr (slot ,4 ,& isnull )))
9991009lrel -> 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. */
10021019if (++ natt >=MaxTupleAttributeNumber )
10031020elog (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. */
10361053Assert (pub_names != NULL );
@@ -1106,10 +1123,12 @@ copy_table(Relation rel)
11061123List * attnamelist ;
11071124ParseState * pstate ;
11081125List * options = NIL ;
1126+ bool gencol_published = false;
11091127
11101128/* Get the publisher relation info. */
11111129fetch_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. */
11151134logicalrep_relmap_update (& lrel );
@@ -1121,8 +1140,8 @@ copy_table(Relation rel)
11211140/* Start copy on the publisher. */
11221141initStringInfo (& cmd );
11231142
1124- /* Regular table with no row filter */
1125- if (lrel .relkind == RELKIND_RELATION && qual == NIL )
1143+ /* Regular table with no row filteror generated columns */
1144+ if (lrel .relkind == RELKIND_RELATION && qual == NIL && ! gencol_published )
11261145{
11271146appendStringInfo (& cmd ,"COPY %s" ,
11281147quote_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 */
11601184appendStringInfoString (& cmd ,"COPY (SELECT " );
11611185for (int i = 0 ;i < lrel .natts ;i ++ )