@@ -150,3 +150,237 @@ SELECT pg_drop_replication_slot('regression_slot3');
150150
151151(1 row)
152152
153+ --
154+ -- Test copy functions for logical replication slots
155+ --
156+ -- Create and copy logical slots
157+ SELECT 'init' FROM pg_create_logical_replication_slot('orig_slot1', 'test_decoding', false);
158+ ?column?
159+ ----------
160+ init
161+ (1 row)
162+
163+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot1', 'copied_slot1_no_change');
164+ ?column?
165+ ----------
166+ copy
167+ (1 row)
168+
169+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot1', 'copied_slot1_change_plugin', false, 'pgoutput');
170+ ?column?
171+ ----------
172+ copy
173+ (1 row)
174+
175+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot1', 'copied_slot1_change_plugin_temp', true, 'pgoutput');
176+ ?column?
177+ ----------
178+ copy
179+ (1 row)
180+
181+ -- Check all copied slots status
182+ SELECT
183+ o.slot_name, o.plugin, o.temporary, c.slot_name, c.plugin, c.temporary
184+ FROM
185+ (SELECT * FROM pg_replication_slots WHERE slot_name LIKE 'orig%') as o
186+ LEFT JOIN pg_replication_slots as c ON o.restart_lsn = c.restart_lsn AND o.confirmed_flush_lsn = c.confirmed_flush_lsn
187+ WHERE
188+ o.slot_name != c.slot_name
189+ ORDER BY o.slot_name, c.slot_name;
190+ slot_name | plugin | temporary | slot_name | plugin | temporary
191+ ------------+---------------+-----------+---------------------------------+---------------+-----------
192+ orig_slot1 | test_decoding | f | copied_slot1_change_plugin | pgoutput | f
193+ orig_slot1 | test_decoding | f | copied_slot1_change_plugin_temp | pgoutput | t
194+ orig_slot1 | test_decoding | f | copied_slot1_no_change | test_decoding | f
195+ (3 rows)
196+
197+ -- Now we have maximum 4 replication slots. Check slots are properly
198+ -- released even when raise error during creating the target slot.
199+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot1', 'failed'); -- error
200+ ERROR: all replication slots are in use
201+ HINT: Free one or increase max_replication_slots.
202+ -- temporary slots were dropped automatically
203+ SELECT pg_drop_replication_slot('orig_slot1');
204+ pg_drop_replication_slot
205+ --------------------------
206+
207+ (1 row)
208+
209+ SELECT pg_drop_replication_slot('copied_slot1_no_change');
210+ pg_drop_replication_slot
211+ --------------------------
212+
213+ (1 row)
214+
215+ SELECT pg_drop_replication_slot('copied_slot1_change_plugin');
216+ pg_drop_replication_slot
217+ --------------------------
218+
219+ (1 row)
220+
221+ -- Test based on the temporary logical slot
222+ SELECT 'init' FROM pg_create_logical_replication_slot('orig_slot2', 'test_decoding', true);
223+ ?column?
224+ ----------
225+ init
226+ (1 row)
227+
228+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot2', 'copied_slot2_no_change');
229+ ?column?
230+ ----------
231+ copy
232+ (1 row)
233+
234+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot2', 'copied_slot2_change_plugin', true, 'pgoutput');
235+ ?column?
236+ ----------
237+ copy
238+ (1 row)
239+
240+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot2', 'copied_slot2_change_plugin_temp', false, 'pgoutput');
241+ ?column?
242+ ----------
243+ copy
244+ (1 row)
245+
246+ -- Check all copied slots status
247+ SELECT
248+ o.slot_name, o.plugin, o.temporary, c.slot_name, c.plugin, c.temporary
249+ FROM
250+ (SELECT * FROM pg_replication_slots WHERE slot_name LIKE 'orig%') as o
251+ LEFT JOIN pg_replication_slots as c ON o.restart_lsn = c.restart_lsn AND o.confirmed_flush_lsn = c.confirmed_flush_lsn
252+ WHERE
253+ o.slot_name != c.slot_name
254+ ORDER BY o.slot_name, c.slot_name;
255+ slot_name | plugin | temporary | slot_name | plugin | temporary
256+ ------------+---------------+-----------+---------------------------------+---------------+-----------
257+ orig_slot2 | test_decoding | t | copied_slot2_change_plugin | pgoutput | t
258+ orig_slot2 | test_decoding | t | copied_slot2_change_plugin_temp | pgoutput | f
259+ orig_slot2 | test_decoding | t | copied_slot2_no_change | test_decoding | t
260+ (3 rows)
261+
262+ -- Cannot copy a logical slot to a physical slot
263+ SELECT 'copy' FROM pg_copy_physical_replication_slot('orig_slot2', 'failed'); -- error
264+ ERROR: cannot copy physical replication slot "orig_slot2" as a logical replication slot
265+ -- temporary slots were dropped automatically
266+ SELECT pg_drop_replication_slot('copied_slot2_change_plugin_temp');
267+ pg_drop_replication_slot
268+ --------------------------
269+
270+ (1 row)
271+
272+ --
273+ -- Test copy functions for physical replication slots
274+ --
275+ -- Create and copy physical slots
276+ SELECT 'init' FROM pg_create_physical_replication_slot('orig_slot1', true);
277+ ?column?
278+ ----------
279+ init
280+ (1 row)
281+
282+ SELECT 'init' FROM pg_create_physical_replication_slot('orig_slot2', false);
283+ ?column?
284+ ----------
285+ init
286+ (1 row)
287+
288+ SELECT 'copy' FROM pg_copy_physical_replication_slot('orig_slot1', 'copied_slot1_no_change');
289+ ?column?
290+ ----------
291+ copy
292+ (1 row)
293+
294+ SELECT 'copy' FROM pg_copy_physical_replication_slot('orig_slot1', 'copied_slot1_temp', true);
295+ ?column?
296+ ----------
297+ copy
298+ (1 row)
299+
300+ -- Check all copied slots status. Since all slots don't reserve WAL we check only other fields.
301+ SELECT slot_name, slot_type, temporary FROM pg_replication_slots;
302+ slot_name | slot_type | temporary
303+ ------------------------+-----------+-----------
304+ orig_slot1 | physical | f
305+ orig_slot2 | physical | f
306+ copied_slot1_no_change | physical | f
307+ copied_slot1_temp | physical | t
308+ (4 rows)
309+
310+ -- Cannot copy a physical slot to a logical slot
311+ SELECT 'copy' FROM pg_copy_logical_replication_slot('orig_slot1', 'failed'); -- error
312+ ERROR: cannot copy logical replication slot "orig_slot1" as a physical replication slot
313+ -- Cannot copy a physical slot that doesn't reserve WAL
314+ SELECT 'copy' FROM pg_copy_physical_replication_slot('orig_slot2', 'failed'); -- error
315+ ERROR: cannot copy a replication slot that doesn't reserve WAL
316+ -- temporary slots were dropped automatically
317+ SELECT pg_drop_replication_slot('orig_slot1');
318+ pg_drop_replication_slot
319+ --------------------------
320+
321+ (1 row)
322+
323+ SELECT pg_drop_replication_slot('orig_slot2');
324+ pg_drop_replication_slot
325+ --------------------------
326+
327+ (1 row)
328+
329+ SELECT pg_drop_replication_slot('copied_slot1_no_change');
330+ pg_drop_replication_slot
331+ --------------------------
332+
333+ (1 row)
334+
335+ -- Test based on the temporary physical slot
336+ SELECT 'init' FROM pg_create_physical_replication_slot('orig_slot2', true, true);
337+ ?column?
338+ ----------
339+ init
340+ (1 row)
341+
342+ SELECT 'copy' FROM pg_copy_physical_replication_slot('orig_slot2', 'copied_slot2_no_change');
343+ ?column?
344+ ----------
345+ copy
346+ (1 row)
347+
348+ SELECT 'copy' FROM pg_copy_physical_replication_slot('orig_slot2', 'copied_slot2_notemp', false);
349+ ?column?
350+ ----------
351+ copy
352+ (1 row)
353+
354+ -- Check all copied slots status
355+ SELECT
356+ o.slot_name, o.temporary, c.slot_name, c.temporary
357+ FROM
358+ (SELECT * FROM pg_replication_slots WHERE slot_name LIKE 'orig%') as o
359+ LEFT JOIN pg_replication_slots as c ON o.restart_lsn = c.restart_lsn
360+ WHERE
361+ o.slot_name != c.slot_name
362+ ORDER BY o.slot_name, c.slot_name;
363+ slot_name | temporary | slot_name | temporary
364+ ------------+-----------+------------------------+-----------
365+ orig_slot2 | t | copied_slot2_no_change | t
366+ orig_slot2 | t | copied_slot2_notemp | f
367+ (2 rows)
368+
369+ SELECT pg_drop_replication_slot('orig_slot2');
370+ pg_drop_replication_slot
371+ --------------------------
372+
373+ (1 row)
374+
375+ SELECT pg_drop_replication_slot('copied_slot2_no_change');
376+ pg_drop_replication_slot
377+ --------------------------
378+
379+ (1 row)
380+
381+ SELECT pg_drop_replication_slot('copied_slot2_notemp');
382+ pg_drop_replication_slot
383+ --------------------------
384+
385+ (1 row)
386+
0 commit comments