mirror of
https://github.com/postgres/postgres.git
synced 2025-12-21 05:21:08 +03:00
Allow specifying column lists for logical replication
This allows specifying an optional column list when adding a table to logical replication. The column list may be specified after the table name, enclosed in parentheses. Columns not included in this list are not sent to the subscriber, allowing the schema on the subscriber to be a subset of the publisher schema. For UPDATE/DELETE publications, the column list needs to cover all REPLICA IDENTITY columns. For INSERT publications, the column list is arbitrary and may omit some REPLICA IDENTITY columns. Furthermore, if the table uses REPLICA IDENTITY FULL, column list is not allowed. The column list can contain only simple column references. Complex expressions, function calls etc. are not allowed. This restriction could be relaxed in the future. During the initial table synchronization, only columns included in the column list are copied to the subscriber. If the subscription has several publications, containing the same table with different column lists, columns specified in any of the lists will be copied. This means all columns are replicated if the table has no column list at all (which is treated as column list with all columns), or when of the publications is defined as FOR ALL TABLES (possibly IN SCHEMA that matches the schema of the table). For partitioned tables, publish_via_partition_root determines whether the column list for the root or the leaf relation will be used. If the parameter is 'false' (the default), the list defined for the leaf relation is used. Otherwise, the column list for the root partition will be used. Psql commands \dRp+ and \d <table-name> now display any column lists. Author: Tomas Vondra, Alvaro Herrera, Rahila Syed Reviewed-by: Peter Eisentraut, Alvaro Herrera, Vignesh C, Ibrar Ahmed, Amit Kapila, Hou zj, Peter Smith, Wang wei, Tang, Shi yu Discussion: https://postgr.es/m/CAH2L28vddB_NFdRVpuyRBJEBWjz4BSyTB=_ektNRH8NJ1jf95g@mail.gmail.com
This commit is contained in:
@@ -29,16 +29,30 @@
|
||||
#define TRUNCATE_CASCADE (1<<0)
|
||||
#define TRUNCATE_RESTART_SEQS (1<<1)
|
||||
|
||||
static void logicalrep_write_attrs(StringInfo out, Relation rel);
|
||||
static void logicalrep_write_attrs(StringInfo out, Relation rel,
|
||||
Bitmapset *columns);
|
||||
static void logicalrep_write_tuple(StringInfo out, Relation rel,
|
||||
TupleTableSlot *slot,
|
||||
bool binary);
|
||||
bool binary, Bitmapset *columns);
|
||||
static void logicalrep_read_attrs(StringInfo in, LogicalRepRelation *rel);
|
||||
static void logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple);
|
||||
|
||||
static void logicalrep_write_namespace(StringInfo out, Oid nspid);
|
||||
static const char *logicalrep_read_namespace(StringInfo in);
|
||||
|
||||
/*
|
||||
* Check if a column is covered by a column list.
|
||||
*
|
||||
* Need to be careful about NULL, which is treated as a column list covering
|
||||
* all columns.
|
||||
*/
|
||||
static bool
|
||||
column_in_column_list(int attnum, Bitmapset *columns)
|
||||
{
|
||||
return (columns == NULL || bms_is_member(attnum, columns));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write BEGIN to the output stream.
|
||||
*/
|
||||
@@ -398,7 +412,7 @@ logicalrep_read_origin(StringInfo in, XLogRecPtr *origin_lsn)
|
||||
*/
|
||||
void
|
||||
logicalrep_write_insert(StringInfo out, TransactionId xid, Relation rel,
|
||||
TupleTableSlot *newslot, bool binary)
|
||||
TupleTableSlot *newslot, bool binary, Bitmapset *columns)
|
||||
{
|
||||
pq_sendbyte(out, LOGICAL_REP_MSG_INSERT);
|
||||
|
||||
@@ -410,7 +424,7 @@ logicalrep_write_insert(StringInfo out, TransactionId xid, Relation rel,
|
||||
pq_sendint32(out, RelationGetRelid(rel));
|
||||
|
||||
pq_sendbyte(out, 'N'); /* new tuple follows */
|
||||
logicalrep_write_tuple(out, rel, newslot, binary);
|
||||
logicalrep_write_tuple(out, rel, newslot, binary, columns);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -443,7 +457,7 @@ logicalrep_read_insert(StringInfo in, LogicalRepTupleData *newtup)
|
||||
void
|
||||
logicalrep_write_update(StringInfo out, TransactionId xid, Relation rel,
|
||||
TupleTableSlot *oldslot, TupleTableSlot *newslot,
|
||||
bool binary)
|
||||
bool binary, Bitmapset *columns)
|
||||
{
|
||||
pq_sendbyte(out, LOGICAL_REP_MSG_UPDATE);
|
||||
|
||||
@@ -464,11 +478,11 @@ logicalrep_write_update(StringInfo out, TransactionId xid, Relation rel,
|
||||
pq_sendbyte(out, 'O'); /* old tuple follows */
|
||||
else
|
||||
pq_sendbyte(out, 'K'); /* old key follows */
|
||||
logicalrep_write_tuple(out, rel, oldslot, binary);
|
||||
logicalrep_write_tuple(out, rel, oldslot, binary, NULL);
|
||||
}
|
||||
|
||||
pq_sendbyte(out, 'N'); /* new tuple follows */
|
||||
logicalrep_write_tuple(out, rel, newslot, binary);
|
||||
logicalrep_write_tuple(out, rel, newslot, binary, columns);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -537,7 +551,7 @@ logicalrep_write_delete(StringInfo out, TransactionId xid, Relation rel,
|
||||
else
|
||||
pq_sendbyte(out, 'K'); /* old key follows */
|
||||
|
||||
logicalrep_write_tuple(out, rel, oldslot, binary);
|
||||
logicalrep_write_tuple(out, rel, oldslot, binary, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -702,7 +716,8 @@ logicalrep_read_sequence(StringInfo in, LogicalRepSequence *seqdata)
|
||||
* Write relation description to the output stream.
|
||||
*/
|
||||
void
|
||||
logicalrep_write_rel(StringInfo out, TransactionId xid, Relation rel)
|
||||
logicalrep_write_rel(StringInfo out, TransactionId xid, Relation rel,
|
||||
Bitmapset *columns)
|
||||
{
|
||||
char *relname;
|
||||
|
||||
@@ -724,7 +739,7 @@ logicalrep_write_rel(StringInfo out, TransactionId xid, Relation rel)
|
||||
pq_sendbyte(out, rel->rd_rel->relreplident);
|
||||
|
||||
/* send the attribute info */
|
||||
logicalrep_write_attrs(out, rel);
|
||||
logicalrep_write_attrs(out, rel, columns);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -801,7 +816,7 @@ logicalrep_read_typ(StringInfo in, LogicalRepTyp *ltyp)
|
||||
*/
|
||||
static void
|
||||
logicalrep_write_tuple(StringInfo out, Relation rel, TupleTableSlot *slot,
|
||||
bool binary)
|
||||
bool binary, Bitmapset *columns)
|
||||
{
|
||||
TupleDesc desc;
|
||||
Datum *values;
|
||||
@@ -813,8 +828,14 @@ logicalrep_write_tuple(StringInfo out, Relation rel, TupleTableSlot *slot,
|
||||
|
||||
for (i = 0; i < desc->natts; i++)
|
||||
{
|
||||
if (TupleDescAttr(desc, i)->attisdropped || TupleDescAttr(desc, i)->attgenerated)
|
||||
Form_pg_attribute att = TupleDescAttr(desc, i);
|
||||
|
||||
if (att->attisdropped || att->attgenerated)
|
||||
continue;
|
||||
|
||||
if (!column_in_column_list(att->attnum, columns))
|
||||
continue;
|
||||
|
||||
nliveatts++;
|
||||
}
|
||||
pq_sendint16(out, nliveatts);
|
||||
@@ -833,6 +854,9 @@ logicalrep_write_tuple(StringInfo out, Relation rel, TupleTableSlot *slot,
|
||||
if (att->attisdropped || att->attgenerated)
|
||||
continue;
|
||||
|
||||
if (!column_in_column_list(att->attnum, columns))
|
||||
continue;
|
||||
|
||||
if (isnull[i])
|
||||
{
|
||||
pq_sendbyte(out, LOGICALREP_COLUMN_NULL);
|
||||
@@ -954,7 +978,7 @@ logicalrep_read_tuple(StringInfo in, LogicalRepTupleData *tuple)
|
||||
* Write relation attribute metadata to the stream.
|
||||
*/
|
||||
static void
|
||||
logicalrep_write_attrs(StringInfo out, Relation rel)
|
||||
logicalrep_write_attrs(StringInfo out, Relation rel, Bitmapset *columns)
|
||||
{
|
||||
TupleDesc desc;
|
||||
int i;
|
||||
@@ -967,8 +991,14 @@ logicalrep_write_attrs(StringInfo out, Relation rel)
|
||||
/* send number of live attributes */
|
||||
for (i = 0; i < desc->natts; i++)
|
||||
{
|
||||
if (TupleDescAttr(desc, i)->attisdropped || TupleDescAttr(desc, i)->attgenerated)
|
||||
Form_pg_attribute att = TupleDescAttr(desc, i);
|
||||
|
||||
if (att->attisdropped || att->attgenerated)
|
||||
continue;
|
||||
|
||||
if (!column_in_column_list(att->attnum, columns))
|
||||
continue;
|
||||
|
||||
nliveatts++;
|
||||
}
|
||||
pq_sendint16(out, nliveatts);
|
||||
@@ -987,6 +1017,9 @@ logicalrep_write_attrs(StringInfo out, Relation rel)
|
||||
if (att->attisdropped || att->attgenerated)
|
||||
continue;
|
||||
|
||||
if (!column_in_column_list(att->attnum, columns))
|
||||
continue;
|
||||
|
||||
/* REPLICA IDENTITY FULL means all columns are sent as part of key. */
|
||||
if (replidentfull ||
|
||||
bms_is_member(att->attnum - FirstLowInvalidHeapAttributeNumber,
|
||||
|
||||
Reference in New Issue
Block a user