1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>29.4. Row Filters</title><link rel="stylesheet" type="text/css" href="stylesheet.css" /><link rev="made" href="pgsql-docs@lists.postgresql.org" /><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot" /><link rel="prev" href="logical-replication-failover.html" title="29.3. Logical Replication Failover" /><link rel="next" href="logical-replication-col-lists.html" title="29.5. Column Lists" /></head><body id="docContent" class="container-fluid col-10"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="5" align="center">29.4. Row Filters</th></tr><tr><td width="10%" align="left"><a accesskey="p" href="logical-replication-failover.html" title="29.3. Logical Replication Failover">Prev</a> </td><td width="10%" align="left"><a accesskey="u" href="logical-replication.html" title="Chapter 29. Logical Replication">Up</a></td><th width="60%" align="center">Chapter 29. Logical Replication</th><td width="10%" align="right"><a accesskey="h" href="index.html" title="PostgreSQL 18.0 Documentation">Home</a></td><td width="10%" align="right"> <a accesskey="n" href="logical-replication-col-lists.html" title="29.5. Column Lists">Next</a></td></tr></table><hr /></div><div class="sect1" id="LOGICAL-REPLICATION-ROW-FILTER"><div class="titlepage"><div><div><h2 class="title" style="clear: both">29.4. Row Filters <a href="#LOGICAL-REPLICATION-ROW-FILTER" class="id_link">#</a></h2></div></div></div><div class="toc"><dl class="toc"><dt><span class="sect2"><a href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-RULES">29.4.1. Row Filter Rules</a></span></dt><dt><span class="sect2"><a href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-RESTRICTIONS">29.4.2. Expression Restrictions</a></span></dt><dt><span class="sect2"><a href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-TRANSFORMATIONS">29.4.3. UPDATE Transformations</a></span></dt><dt><span class="sect2"><a href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-PARTITIONED-TABLE">29.4.4. Partitioned Tables</a></span></dt><dt><span class="sect2"><a href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-INITIAL-DATA-SYNC">29.4.5. Initial Data Synchronization</a></span></dt><dt><span class="sect2"><a href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-COMBINING">29.4.6. Combining Multiple Row Filters</a></span></dt><dt><span class="sect2"><a href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-EXAMPLES">29.4.7. Examples</a></span></dt></dl></div><p>
3 By default, all data from all published tables will be replicated to the
4 appropriate subscribers. The replicated data can be reduced by using a
5 <em class="firstterm">row filter</em>. A user might choose to use row filters
6 for behavioral, security or performance reasons. If a published table sets a
7 row filter, a row is replicated only if its data satisfies the row filter
8 expression. This allows a set of tables to be partially replicated. The row
9 filter is defined per table. Use a <code class="literal">WHERE</code> clause after the
10 table name for each published table that requires data to be filtered out.
11 The <code class="literal">WHERE</code> clause must be enclosed by parentheses. See
12 <a class="xref" href="sql-createpublication.html" title="CREATE PUBLICATION"><span class="refentrytitle">CREATE PUBLICATION</span></a> for details.
13 </p><div class="sect2" id="LOGICAL-REPLICATION-ROW-FILTER-RULES"><div class="titlepage"><div><div><h3 class="title">29.4.1. Row Filter Rules <a href="#LOGICAL-REPLICATION-ROW-FILTER-RULES" class="id_link">#</a></h3></div></div></div><p>
14 Row filters are applied <span class="emphasis"><em>before</em></span> publishing the changes.
15 If the row filter evaluates to <code class="literal">false</code> or <code class="literal">NULL</code>
16 then the row is not replicated. The <code class="literal">WHERE</code> clause expression
17 is evaluated with the same role used for the replication connection (i.e.
18 the role specified in the
19 <a class="link" href="sql-createsubscription.html#SQL-CREATESUBSCRIPTION-PARAMS-CONNECTION"><code class="literal">CONNECTION</code></a>
20 clause of the <a class="xref" href="sql-createsubscription.html" title="CREATE SUBSCRIPTION"><span class="refentrytitle">CREATE SUBSCRIPTION</span></a>). Row filters have
21 no effect for <code class="command">TRUNCATE</code> command.
22 </p></div><div class="sect2" id="LOGICAL-REPLICATION-ROW-FILTER-RESTRICTIONS"><div class="titlepage"><div><div><h3 class="title">29.4.2. Expression Restrictions <a href="#LOGICAL-REPLICATION-ROW-FILTER-RESTRICTIONS" class="id_link">#</a></h3></div></div></div><p>
23 The <code class="literal">WHERE</code> clause allows only simple expressions. It
24 cannot contain user-defined functions, operators, types, and collations,
25 system column references or non-immutable built-in functions.
27 If a publication publishes <code class="command">UPDATE</code> or
28 <code class="command">DELETE</code> operations, the row filter <code class="literal">WHERE</code>
29 clause must contain only columns that are covered by the replica identity
30 (see <a class="xref" href="sql-altertable.html#SQL-ALTERTABLE-REPLICA-IDENTITY"><code class="literal">REPLICA IDENTITY</code></a>). If a publication
31 publishes only <code class="command">INSERT</code> operations, the row filter
32 <code class="literal">WHERE</code> clause can use any column.
33 </p></div><div class="sect2" id="LOGICAL-REPLICATION-ROW-FILTER-TRANSFORMATIONS"><div class="titlepage"><div><div><h3 class="title">29.4.3. UPDATE Transformations <a href="#LOGICAL-REPLICATION-ROW-FILTER-TRANSFORMATIONS" class="id_link">#</a></h3></div></div></div><p>
34 Whenever an <code class="command">UPDATE</code> is processed, the row filter
35 expression is evaluated for both the old and new row (i.e. using the data
36 before and after the update). If both evaluations are <code class="literal">true</code>,
37 it replicates the <code class="command">UPDATE</code> change. If both evaluations are
38 <code class="literal">false</code>, it doesn't replicate the change. If only one of
39 the old/new rows matches the row filter expression, the <code class="command">UPDATE</code>
40 is transformed to <code class="command">INSERT</code> or <code class="command">DELETE</code>, to
41 avoid any data inconsistency. The row on the subscriber should reflect what
42 is defined by the row filter expression on the publisher.
44 If the old row satisfies the row filter expression (it was sent to the
45 subscriber) but the new row doesn't, then, from a data consistency
46 perspective the old row should be removed from the subscriber.
47 So the <code class="command">UPDATE</code> is transformed into a <code class="command">DELETE</code>.
49 If the old row doesn't satisfy the row filter expression (it wasn't sent
50 to the subscriber) but the new row does, then, from a data consistency
51 perspective the new row should be added to the subscriber.
52 So the <code class="command">UPDATE</code> is transformed into an <code class="command">INSERT</code>.
54 <a class="xref" href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-TRANSFORMATIONS-SUMMARY" title="Table 29.1. UPDATE Transformation Summary">Table 29.1</a>
55 summarizes the applied transformations.
56 </p><div class="table" id="LOGICAL-REPLICATION-ROW-FILTER-TRANSFORMATIONS-SUMMARY"><p class="title"><strong>Table 29.1. <code class="command">UPDATE</code> Transformation Summary</strong></p><div class="table-contents"><table class="table" summary="UPDATE Transformation Summary" border="1"><colgroup><col /><col /><col /></colgroup><thead><tr><th>Old row</th><th>New row</th><th>Transformation</th></tr></thead><tbody><tr><td>no match</td><td>no match</td><td>don't replicate</td></tr><tr><td>no match</td><td>match</td><td><code class="literal">INSERT</code></td></tr><tr><td>match</td><td>no match</td><td><code class="literal">DELETE</code></td></tr><tr><td>match</td><td>match</td><td><code class="literal">UPDATE</code></td></tr></tbody></table></div></div><br class="table-break" /></div><div class="sect2" id="LOGICAL-REPLICATION-ROW-FILTER-PARTITIONED-TABLE"><div class="titlepage"><div><div><h3 class="title">29.4.4. Partitioned Tables <a href="#LOGICAL-REPLICATION-ROW-FILTER-PARTITIONED-TABLE" class="id_link">#</a></h3></div></div></div><p>
57 If the publication contains a partitioned table, the publication parameter
58 <a class="link" href="sql-createpublication.html#SQL-CREATEPUBLICATION-PARAMS-WITH-PUBLISH-VIA-PARTITION-ROOT"><code class="literal">publish_via_partition_root</code></a>
59 determines which row filter is used. If <code class="literal">publish_via_partition_root</code>
60 is <code class="literal">true</code>, the <span class="emphasis"><em>root partitioned table's</em></span>
61 row filter is used. Otherwise, if <code class="literal">publish_via_partition_root</code>
62 is <code class="literal">false</code> (default), each <span class="emphasis"><em>partition's</em></span>
64 </p></div><div class="sect2" id="LOGICAL-REPLICATION-ROW-FILTER-INITIAL-DATA-SYNC"><div class="titlepage"><div><div><h3 class="title">29.4.5. Initial Data Synchronization <a href="#LOGICAL-REPLICATION-ROW-FILTER-INITIAL-DATA-SYNC" class="id_link">#</a></h3></div></div></div><p>
65 If the subscription requires copying pre-existing table data
66 and a publication contains <code class="literal">WHERE</code> clauses, only data that
67 satisfies the row filter expressions is copied to the subscriber.
69 If the subscription has several publications in which a table has been
70 published with different <code class="literal">WHERE</code> clauses, rows that satisfy
71 <span class="emphasis"><em>any</em></span> of the expressions will be copied. See
72 <a class="xref" href="logical-replication-row-filter.html#LOGICAL-REPLICATION-ROW-FILTER-COMBINING" title="29.4.6. Combining Multiple Row Filters">Section 29.4.6</a> for details.
73 </p><div class="warning"><h3 class="title">Warning</h3><p>
74 Because initial data synchronization does not take into account the
75 <a class="link" href="sql-createpublication.html#SQL-CREATEPUBLICATION-PARAMS-WITH-PUBLISH"><code class="literal">publish</code></a>
76 parameter when copying existing table data, some rows may be copied that
77 would not be replicated using DML. Refer to
78 <a class="xref" href="logical-replication-architecture.html#LOGICAL-REPLICATION-SNAPSHOT" title="29.9.1. Initial Snapshot">Section 29.9.1</a>, and see
79 <a class="xref" href="logical-replication-subscription.html#LOGICAL-REPLICATION-SUBSCRIPTION-EXAMPLES" title="29.2.2. Examples: Set Up Logical Replication">Section 29.2.2</a> for examples.
80 </p></div><div class="note"><h3 class="title">Note</h3><p>
81 If the subscriber is in a release prior to 15, copy pre-existing data
82 doesn't use row filters even if they are defined in the publication.
83 This is because old releases can only copy the entire table data.
84 </p></div></div><div class="sect2" id="LOGICAL-REPLICATION-ROW-FILTER-COMBINING"><div class="titlepage"><div><div><h3 class="title">29.4.6. Combining Multiple Row Filters <a href="#LOGICAL-REPLICATION-ROW-FILTER-COMBINING" class="id_link">#</a></h3></div></div></div><p>
85 If the subscription has several publications in which the same table has
86 been published with different row filters (for the same
87 <a class="link" href="sql-createpublication.html#SQL-CREATEPUBLICATION-PARAMS-WITH-PUBLISH"><code class="literal">publish</code></a>
88 operation), those expressions get ORed together, so that rows satisfying
89 <span class="emphasis"><em>any</em></span> of the expressions will be replicated. This means all
90 the other row filters for the same table become redundant if:
91 </p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
92 One of the publications has no row filter.
93 </p></li><li class="listitem"><p>
94 One of the publications was created using
95 <a class="link" href="sql-createpublication.html#SQL-CREATEPUBLICATION-PARAMS-FOR-ALL-TABLES"><code class="literal">FOR ALL TABLES</code></a>.
96 This clause does not allow row filters.
97 </p></li><li class="listitem"><p>
98 One of the publications was created using
99 <a class="link" href="sql-createpublication.html#SQL-CREATEPUBLICATION-PARAMS-FOR-TABLES-IN-SCHEMA"><code class="literal">FOR TABLES IN SCHEMA</code></a>
100 and the table belongs to the referred schema. This clause does not allow
102 </p></li></ul></div></div><div class="sect2" id="LOGICAL-REPLICATION-ROW-FILTER-EXAMPLES"><div class="titlepage"><div><div><h3 class="title">29.4.7. Examples <a href="#LOGICAL-REPLICATION-ROW-FILTER-EXAMPLES" class="id_link">#</a></h3></div></div></div><p>
103 Create some tables to be used in the following examples.
104 </p><pre class="programlisting">
105 /* pub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
106 /* pub # */ CREATE TABLE t2(d int, e int, f int, PRIMARY KEY(d));
107 /* pub # */ CREATE TABLE t3(g int, h int, i int, PRIMARY KEY(g));
109 Create some publications. Publication <code class="literal">p1</code> has one table
110 (<code class="literal">t1</code>) and that table has a row filter. Publication
111 <code class="literal">p2</code> has two tables. Table <code class="literal">t1</code> has no row
112 filter, and table <code class="literal">t2</code> has a row filter. Publication
113 <code class="literal">p3</code> has two tables, and both of them have a row filter.
114 </p><pre class="programlisting">
115 /* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 WHERE (a > 5 AND c = 'NSW');
116 /* pub # */ CREATE PUBLICATION p2 FOR TABLE t1, t2 WHERE (e = 99);
117 /* pub # */ CREATE PUBLICATION p3 FOR TABLE t2 WHERE (d = 10), t3 WHERE (g = 10);
119 <code class="command">psql</code> can be used to show the row filter expressions (if
120 defined) for each publication.
121 </p><pre class="programlisting">
124 Owner | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
125 ----------+------------+---------+---------+---------+-----------+-------------------+----------
126 postgres | f | t | t | t | t | none | f
128 "public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))
131 Owner | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
132 ----------+------------+---------+---------+---------+-----------+-------------------+----------
133 postgres | f | t | t | t | t | none | f
136 "public.t2" WHERE (e = 99)
139 Owner | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
140 ----------+------------+---------+---------+---------+-----------+-------------------+----------
141 postgres | f | t | t | t | t | none | f
143 "public.t2" WHERE (d = 10)
144 "public.t3" WHERE (g = 10)
146 <code class="command">psql</code> can be used to show the row filter expressions (if
147 defined) for each table. See that table <code class="literal">t1</code> is a member
148 of two publications, but has a row filter only in <code class="literal">p1</code>.
149 See that table <code class="literal">t2</code> is a member of two publications, and
150 has a different row filter in each of them.
151 </p><pre class="programlisting">
154 Column | Type | Collation | Nullable | Default
155 --------+---------+-----------+----------+---------
156 a | integer | | not null |
158 c | text | | not null |
160 "t1_pkey" PRIMARY KEY, btree (a, c)
162 "p1" WHERE ((a > 5) AND (c = 'NSW'::text))
167 Column | Type | Collation | Nullable | Default
168 --------+---------+-----------+----------+---------
169 d | integer | | not null |
173 "t2_pkey" PRIMARY KEY, btree (d)
180 Column | Type | Collation | Nullable | Default
181 --------+---------+-----------+----------+---------
182 g | integer | | not null |
186 "t3_pkey" PRIMARY KEY, btree (g)
190 On the subscriber node, create a table <code class="literal">t1</code> with the same
191 definition as the one on the publisher, and also create the subscription
192 <code class="literal">s1</code> that subscribes to the publication <code class="literal">p1</code>.
193 </p><pre class="programlisting">
194 /* sub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
195 /* sub # */ CREATE SUBSCRIPTION s1
196 /* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s1'
197 /* sub - */ PUBLICATION p1;
199 Insert some rows. Only the rows satisfying the <code class="literal">t1 WHERE</code>
200 clause of publication <code class="literal">p1</code> are replicated.
201 </p><pre class="programlisting">
202 /* pub # */ INSERT INTO t1 VALUES (2, 102, 'NSW');
203 /* pub # */ INSERT INTO t1 VALUES (3, 103, 'QLD');
204 /* pub # */ INSERT INTO t1 VALUES (4, 104, 'VIC');
205 /* pub # */ INSERT INTO t1 VALUES (5, 105, 'ACT');
206 /* pub # */ INSERT INTO t1 VALUES (6, 106, 'NSW');
207 /* pub # */ INSERT INTO t1 VALUES (7, 107, 'NT');
208 /* pub # */ INSERT INTO t1 VALUES (8, 108, 'QLD');
209 /* pub # */ INSERT INTO t1 VALUES (9, 109, 'NSW');
211 /* pub # */ SELECT * FROM t1;
224 </p><pre class="programlisting">
225 /* sub # */ SELECT * FROM t1;
232 Update some data, where the old and new row values both
233 satisfy the <code class="literal">t1 WHERE</code> clause of publication
234 <code class="literal">p1</code>. The <code class="command">UPDATE</code> replicates
235 the change as normal.
236 </p><pre class="programlisting">
237 /* pub # */ UPDATE t1 SET b = 999 WHERE a = 6;
239 /* pub # */ SELECT * FROM t1;
252 </p><pre class="programlisting">
253 /* sub # */ SELECT * FROM t1;
260 Update some data, where the old row values did not satisfy
261 the <code class="literal">t1 WHERE</code> clause of publication <code class="literal">p1</code>,
262 but the new row values do satisfy it. The <code class="command">UPDATE</code> is
263 transformed into an <code class="command">INSERT</code> and the change is replicated.
264 See the new row on the subscriber.
265 </p><pre class="programlisting">
266 /* pub # */ UPDATE t1 SET a = 555 WHERE a = 2;
268 /* pub # */ SELECT * FROM t1;
281 </p><pre class="programlisting">
282 /* sub # */ SELECT * FROM t1;
290 Update some data, where the old row values satisfied
291 the <code class="literal">t1 WHERE</code> clause of publication <code class="literal">p1</code>,
292 but the new row values do not satisfy it. The <code class="command">UPDATE</code> is
293 transformed into a <code class="command">DELETE</code> and the change is replicated.
294 See that the row is removed from the subscriber.
295 </p><pre class="programlisting">
296 /* pub # */ UPDATE t1 SET c = 'VIC' WHERE a = 9;
298 /* pub # */ SELECT * FROM t1;
311 </p><pre class="programlisting">
312 /* sub # */ SELECT * FROM t1;
319 The following examples show how the publication parameter
320 <a class="link" href="sql-createpublication.html#SQL-CREATEPUBLICATION-PARAMS-WITH-PUBLISH-VIA-PARTITION-ROOT"><code class="literal">publish_via_partition_root</code></a>
321 determines whether the row filter of the parent or child table will be used
322 in the case of partitioned tables.
324 Create a partitioned table on the publisher.
325 </p><pre class="programlisting">
326 /* pub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
327 /* pub # */ CREATE TABLE child PARTITION OF parent DEFAULT;
329 Create the same tables on the subscriber.
330 </p><pre class="programlisting">
331 /* sub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
332 /* sub # */ CREATE TABLE child PARTITION OF parent DEFAULT;
334 Create a publication <code class="literal">p4</code>, and then subscribe to it. The
335 publication parameter <code class="literal">publish_via_partition_root</code> is set
336 as true. There are row filters defined on both the partitioned table
337 (<code class="literal">parent</code>), and on the partition (<code class="literal">child</code>).
338 </p><pre class="programlisting">
339 /* pub # */ CREATE PUBLICATION p4 FOR TABLE parent WHERE (a < 5), child WHERE (a >= 5)
340 /* pub - */ WITH (publish_via_partition_root=true);
342 </p><pre class="programlisting">
343 /* sub # */ CREATE SUBSCRIPTION s4
344 /* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s4'
345 /* sub - */ PUBLICATION p4;
347 Insert some values directly into the <code class="literal">parent</code> and
348 <code class="literal">child</code> tables. They replicate using the row filter of
349 <code class="literal">parent</code> (because <code class="literal">publish_via_partition_root</code>
351 </p><pre class="programlisting">
352 /* pub # */ INSERT INTO parent VALUES (2), (4), (6);
353 /* pub # */ INSERT INTO child VALUES (3), (5), (7);
355 /* pub # */ SELECT * FROM parent ORDER BY a;
366 </p><pre class="programlisting">
367 /* sub # */ SELECT * FROM parent ORDER BY a;
375 Repeat the same test, but with a different value for <code class="literal">publish_via_partition_root</code>.
376 The publication parameter <code class="literal">publish_via_partition_root</code> is
377 set as false. A row filter is defined on the partition (<code class="literal">child</code>).
378 </p><pre class="programlisting">
379 /* pub # */ DROP PUBLICATION p4;
380 /* pub # */ CREATE PUBLICATION p4 FOR TABLE parent, child WHERE (a >= 5)
381 /* pub - */ WITH (publish_via_partition_root=false);
383 </p><pre class="programlisting">
384 /* sub # */ ALTER SUBSCRIPTION s4 REFRESH PUBLICATION;
386 Do the inserts on the publisher same as before. They replicate using the
387 row filter of <code class="literal">child</code> (because
388 <code class="literal">publish_via_partition_root</code> is false).
389 </p><pre class="programlisting">
390 /* pub # */ TRUNCATE parent;
391 /* pub # */ INSERT INTO parent VALUES (2), (4), (6);
392 /* pub # */ INSERT INTO child VALUES (3), (5), (7);
394 /* pub # */ SELECT * FROM parent ORDER BY a;
405 </p><pre class="programlisting">
406 /* sub # */ SELECT * FROM child ORDER BY a;
413 </pre></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="logical-replication-failover.html" title="29.3. Logical Replication Failover">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="logical-replication.html" title="Chapter 29. Logical Replication">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="logical-replication-col-lists.html" title="29.5. Column Lists">Next</a></td></tr><tr><td width="40%" align="left" valign="top">29.3. Logical Replication Failover </td><td width="20%" align="center"><a accesskey="h" href="index.html" title="PostgreSQL 18.0 Documentation">Home</a></td><td width="40%" align="right" valign="top"> 29.5. Column Lists</td></tr></table></div></body></html>