PostgreSQL 15+ supports filtering which rows and columns are replicated, enabling selective data distribution.

Row Filtering

Row filters use WHERE clauses to replicate only matching rows:

sql
-- Only replicate active users
CREATE PUBLICATION active_users FOR TABLE users
    WHERE (status = 'active');

-- Only replicate orders from specific region
CREATE PUBLICATION us_orders FOR TABLE orders
    WHERE (region = 'US' AND amount > 100);

-- Multiple tables with different filters
CREATE PUBLICATION regional FOR TABLE 
    orders WHERE (region = 'EU'),
    customers WHERE (country IN ('DE', 'FR', 'IT'));

Row Filter Limitations

  • Only simple expressions allowed (no subqueries, aggregates)
  • Functions must be IMMUTABLE
  • Cannot reference system columns
  • UPDATE/DELETE only work if filter column is in REPLICA IDENTITY
sql
-- Set REPLICA IDENTITY to include filter column
ALTER TABLE orders REPLICA IDENTITY USING INDEX orders_region_idx;

Column Filtering (Column Lists)

sql
-- Replicate only specific columns
CREATE PUBLICATION partial_users FOR TABLE users (id, email, name);

-- Exclude sensitive columns
-- (password_hash, ssn are NOT replicated)
CREATE PUBLICATION safe_users FOR TABLE users (id, name, email, created_at);

Combining Row and Column Filters

sql
-- Replicate specific columns for specific rows
CREATE PUBLICATION filtered FOR TABLE 
    users (id, name, email) WHERE (is_public = true);

Important: Primary key columns are always included even if not in column list.

Code Examples

typescript
-- Multi-tenant filtering: only replicate tenant's data
CREATE PUBLICATION tenant_123 FOR TABLE 
    orders WHERE (tenant_id = 123),
    customers WHERE (tenant_id = 123),
    products WHERE (tenant_id = 123);

-- Column filtering for privacy
CREATE PUBLICATION analytics FOR TABLE 
    users (id, created_at, country, signup_source);  -- No PII

-- Combined filtering
CREATE PUBLICATION eu_customers FOR TABLE 
    customers (id, email, country, created_at) 
    WHERE (gdpr_consent = true AND country IN ('DE', 'FR', 'IT'));
✓ Completed