Engineer selective logical replication with row filters, column lists, PostgreSQL 18 generated-column behavior, partition-root semantics, and explicit multi-database integration boundaries.

Row Filters, Column Lists, Selective Replication, and Multi-Database Integration Patterns

Engineer selective logical replication with row filters, column lists, PostgreSQL 18 generated-column behavior, partition-root semantics, and explicit multi-database integration boundaries.

Intermediate → Advanced180–240 minutesLogical replication and data-movement labCurrent patched PostgreSQL 18.xCore PostgreSQL only for mandatory workDisposable local publisher/subscriber on ports 55439–55440Optional third node on port 55441 only where explicitly statedReplication, database CREATE, and table ownership privileges where statedLast reviewed: August 2026

Learning outcomes

ServiceHub does not always need every row and every column downstream. An analytics database might need only northern-region work orders and operational fields, while a migration target might need all rows but omit a source-only column. PostgreSQL publications can express those choices, but they are selection mechanisms, not a transformation engine.

01

Apply row filters before publication and explain UPDATE boundary-crossing behavior.

02

Use publication column lists while preserving replica-identity requirements.

03

Explain why filters and column lists are not a security boundary against a malicious subscriber.

04

Apply PostgreSQL 18 generated-column publication semantics correctly.

05

Design fan-out and integration patterns without assuming automatic schema transformation.

1. Row filters select source rows

A row filter is a WHERE expression attached to a published table. If it evaluates false or null, the change is not published. Filters are evaluated on the publisher before the change is sent. They use a deliberately restricted expression language: simple immutable expressions, no user-defined functions/operators/types, no system columns, and no non-immutable built-ins.

sql · deliberately invalid filter before replica-identity repair
CREATE PUBLICATION ch15_north_pubFOR TABLE app.ch15_work_ordersWHERE (region = 'north');

For publications that include UPDATE or DELETE, every column used by the row filter must be covered by replica identity. The current ch15_work_orders primary key contains only work_order_id, so a filter on region would be rejected for update/delete publication semantics. A design option is a suitable replica-identity unique index that includes the required identity columns, or a publication that only publishes INSERT if that truly matches the use case.

sql · publisher: create a filter-compatible replica identity
CREATE UNIQUE INDEX ch15_work_orders_repl_identityON app.ch15_work_orders (work_order_id, region);ALTER TABLE app.ch15_work_ordersREPLICA IDENTITY USING INDEX ch15_work_orders_repl_identity;DROP PUBLICATION IF EXISTS ch15_north_pub;CREATE PUBLICATION ch15_north_pubFOR TABLE app.ch15_work_ordersWHERE (region = 'north');

2. UPDATE across a filter boundary can change operation shape

Suppose a row currently matches region='north' and an update moves it to south. From the subscriber's selective viewpoint the row has left the published set, so PostgreSQL can logically produce a delete-like effect. The reverse transition can appear insert-like. This is why row-filtered consumers must reason about set membership, not just the SQL verb originally issued by the publisher application.

sql · publisher: move a row out of and back into the filter
UPDATE app.ch15_work_ordersSET region = 'south', changed_at = clock_timestamp()WHERE work_order_id = 15001;UPDATE app.ch15_work_ordersSET region = 'north', changed_at = clock_timestamp()WHERE work_order_id = 15001;

Validate the subscriber's resulting set, not merely whether the source UPDATE committed. A row filter has no effect on TRUNCATE, which is another reason to treat publication operation choices deliberately.

3. Column lists select attributes—not schemas

A publication can publish only selected columns. The subscriber table must contain at least those columns, matched by name. Column order need not match. For UPDATE/DELETE publications, the column list must include replica-identity columns.

sql · publisher: publish a narrow operational projection
CREATE PUBLICATION ch15_ops_pubFOR TABLE app.ch15_work_orders  (work_order_id, region, status, changed_at)WHERE (region = 'north');

A column list is not a durable security boundary. PostgreSQL has no publication privilege that prevents a sufficiently capable subscriber from using another accessible publication to request more data. Protect sensitive source data with publisher-side roles, network policy, database design, and controlled publication ownership—not merely a narrow column list.

Important distinction

A column list can reduce replicated payload and define a clean downstream contract. It does not rename columns, calculate arbitrary new values, join tables, or execute ETL logic. If you need transformation, use an explicitly designed staging/CDC pipeline or materialized source table.

4. PostgreSQL 18 generated-column behavior is version-sensitive

PostgreSQL 18 added the ability to publish stored generated-column values. By default generated columns are not published; if both publisher and subscriber define the column as generated, the subscriber computes its own generated value. PostgreSQL 18 can instead publish a stored generated column through a column list or publish_generated_columns='stored'.

sql · publisher: a stored generated value
ALTER TABLE app.ch15_work_ordersADD COLUMN labor_hours numericGENERATED ALWAYS AS (labor_minutes / 60.0) STORED;-- Publish the generated value explicitly as part of a column contract:CREATE PUBLICATION ch15_generated_pubFOR TABLE app.ch15_work_orders  (work_order_id, labor_minutes, labor_hours);

If the generated value is actually published, the subscriber target for that published value must be a regular column; publishing a generated value into another generated column is not supported. Virtual generated columns are not publishable in this feature. A subscriber older than PostgreSQL 18 also has initial-copy limitations for generated columns.

5. Partitioned tables: leaf identity or root identity?

By default, changes originate using each publisher leaf partition's identity and schema. publish_via_partition_root=true changes that contract so the partitioned root's identity and schema are published. This can be useful when the subscriber has a different partition layout—or even a non-partitioned target table—but it also changes which row filter and column list apply.

sql · publication option to use root-table identity
CREATE PUBLICATION ch15_partition_root_pubWITH (publish_via_partition_root = true);-- Add a partitioned table only after its target contract is designed:-- ALTER PUBLICATION ch15_partition_root_pub ADD TABLE app.some_partitioned_table;

Do not assume that logical replication automatically creates, attaches, or routes subscriber partitions. The subscriber must provide a compatible target schema.

6. Fan-out and multi-database integration patterns

One publication can have multiple subscribers. A subscriber can also publish onward, creating logical chains, but that does not automatically prevent loops or guarantee a coherent multi-master conflict policy. Replication origins help track where changes came from, while application/topology design must decide ownership of each write domain.

A safe ServiceHub fan-out design might publish an operational subset to an analytics database and a complete dataset to a migration target. Keep those contracts as separate publications so row/column scope, retention, and cutover ownership can be reasoned about independently.

sql · publisher: inspect effective publication contracts
SELECT pubname, schemaname, tablename,       attnames, rowfilterFROM pg_publication_tablesWHERE pubname IN ('ch15_ops_pub','ch15_generated_pub','ch15_servicehub_pub')ORDER BY pubname, schemaname, tablename;

The exact catalog rendering of column names and row filters is useful evidence during change review. Treat publication definitions as production configuration that should be version-controlled and peer-reviewed.

7. Wrong assumption: logical replication is schema mapping

Deliberately wrong approach

Creating a subscriber table named app.work_orders_archive and expecting PostgreSQL to map app.ch15_work_orders into it will fail. Built-in subscriptions match tables by fully-qualified name. If the integration contract requires renamed tables or arbitrary transformations, build an explicit ETL/CDC consumer or stage the data into same-named tables first.

Even when source and target table names match, text-mode replication only requires convertible target types, while binary mode has stricter compatibility requirements. Cross-version migration therefore deserves explicit type and extension compatibility testing.

8. Lab verification

sql · subscriber: verify only intended rows/columns after creating a matching selective subscription
SELECT work_order_id, region, status, changed_atFROM app.ch15_work_ordersORDER BY work_order_id;-- Compare with publisher contract:-- north rows should be present for ch15_ops_pub;-- unpublished attributes should not be assumed synchronized.

Check your understanding

  1. Why must UPDATE/DELETE row-filter columns be covered by replica identity?
  2. What can happen when an UPDATE moves a row across a row-filter boundary?
  3. Why are column lists not a strong security boundary?
  4. What changed for generated-column logical replication in PostgreSQL 18?
  5. What does publish_via_partition_root change?
Review the answers

Row filters must have enough identity information to determine old/new membership for UPDATE and DELETE. Crossing a filter boundary can become insert-like or delete-like from the subscriber's perspective. Column lists constrain a publication contract but do not prevent an authorized subscriber from accessing another publication. PostgreSQL 18 can publish stored generated values explicitly; virtual generated values remain excluded. publish_via_partition_root uses the partition root's identity/schema and affects filter/column-list selection.

Authoritative references

Logical replication is version-, privilege-, topology-, and schema-sensitive. These PostgreSQL 18 primary sources define the behavior used in this lesson.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.