├── debian ├── compat ├── source │ └── format ├── tests │ ├── installcheck │ └── control ├── pgversions ├── watch ├── rules ├── control.in └── control ├── expected └── check.out ├── sql └── check.sql ├── pg_tm_aux.control ├── Makefile ├── pg_tm_aux--1.0.sql ├── pg_tm_aux--1.0--1.1.sql ├── LICENSE ├── pg_tm_aux--1.1--1.1.1.sql ├── README.md └── pg_tm_aux.c /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/tests/installcheck: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pg_buildext installcheck 3 | -------------------------------------------------------------------------------- /debian/pgversions: -------------------------------------------------------------------------------- 1 | 10 2 | 11 3 | 12 4 | 13 5 | 14 6 | 15 7 | 16 8 | 17 9 | -------------------------------------------------------------------------------- /expected/check.out: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION pg_tm_aux; 2 | -- I have no idea how to test it 3 | -------------------------------------------------------------------------------- /sql/check.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION pg_tm_aux; 2 | 3 | -- I have no idea how to test it 4 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | https://github.com/postgrespro/jsquery/releases .*/ver_(.*).tar.gz 3 | -------------------------------------------------------------------------------- /debian/tests/control: -------------------------------------------------------------------------------- 1 | Depends: @, postgresql-server-dev-all 2 | Tests: installcheck 3 | Restrictions: allow-stderr 4 | -------------------------------------------------------------------------------- /pg_tm_aux.control: -------------------------------------------------------------------------------- 1 | # pg_tm_aux extension 2 | comment = 'transfer manager auxilary functions' 3 | default_version = '1.1' 4 | module_pathname = '$libdir/pg_tm_aux' 5 | relocatable = true 6 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | override_dh_auto_build: 4 | +pg_buildext build build-%v 5 | 6 | override_dh_auto_test: 7 | # nothing to do here, see debian/tests/* instead 8 | 9 | override_dh_auto_install: 10 | +pg_buildext loop pg-tm-aux-%v 11 | 12 | override_dh_auto_clean: 13 | +pg_buildext clean build-%v 14 | rm -rf pg_tm_aux 15 | 16 | %: 17 | dh $@ 18 | -------------------------------------------------------------------------------- /debian/control.in: -------------------------------------------------------------------------------- 1 | Source: pg_tm_aux 2 | Section: database 3 | Priority: optional 4 | Maintainer: Andrey Borodin (Yandex.Cloud) 5 | Build-Depends: debhelper (>= 9), postgresql-server-dev-all (>= 195~), git 6 | Standards-Version: 1.1-0 7 | Homepage: https://github.com/x4m/pg_tm_aux 8 | 9 | Package: postgresql-PGVERSION-pg-tm-aux 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-PGVERSION 12 | Description: Extension for PostgreSQL for collecting statistics about messages in logfile 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # contrib/pg_tm_aux/Makefile 2 | 3 | MODULE_big = pg_tm_aux 4 | OBJS = \ 5 | $(WIN32RES) \ 6 | pg_tm_aux.o 7 | 8 | EXTENSION = pg_tm_aux 9 | DATA = pg_tm_aux--1.0.sql pg_tm_aux--1.0--1.1.sql pg_tm_aux--1.1--1.1.1.sql 10 | PGFILEDESC = "pg_tm_aux - transfer manager auxilary functions" 11 | 12 | REGRESS = check 13 | 14 | ifdef USE_PGXS 15 | PG_CONFIG = pg_config 16 | PGXS := $(shell $(PG_CONFIG) --pgxs) 17 | include $(PGXS) 18 | else 19 | subdir = contrib/pg_tm_aux 20 | top_builddir = ../.. 21 | include $(top_builddir)/src/Makefile.global 22 | include $(top_srcdir)/contrib/contrib-global.mk 23 | endif 24 | -------------------------------------------------------------------------------- /pg_tm_aux--1.0.sql: -------------------------------------------------------------------------------- 1 | /* contrib/pg_tm_aux/pg_tm_aux--1.0.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION pg_tm_aux" to load this file. \quit 5 | 6 | -- 7 | -- pg_create_logical_replication_slot_lsn() 8 | -- 9 | CREATE OR REPLACE FUNCTION pg_create_logical_replication_slot_lsn( 10 | IN slot_name name, IN plugin name, 11 | IN temporary boolean DEFAULT false, IN restart_lsn pg_lsn DEFAULT null, 12 | OUT slot_name name, OUT lsn pg_lsn) 13 | RETURNS RECORD 14 | LANGUAGE C 15 | STRICT VOLATILE 16 | AS 'MODULE_PATHNAME', 'pg_create_logical_replication_slot_lsn'; 17 | 18 | -- NOTE: pg_create_logical_replication_slot_lsn() checks permissions internally, no need to worry here -------------------------------------------------------------------------------- /pg_tm_aux--1.0--1.1.sql: -------------------------------------------------------------------------------- 1 | /* contrib/pg_tm_aux/pg_tm_aux--1.0--1.1.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION pg_tm_aux" to load this file. \quit 5 | 6 | -- 7 | -- pg_create_logical_replication_slot_lsn() 8 | -- 9 | CREATE OR REPLACE FUNCTION pg_create_logical_replication_slot_lsn( 10 | IN slot_name name, IN plugin name, 11 | IN temporary boolean DEFAULT false, IN restart_lsn pg_lsn DEFAULT null, 12 | IN force boolean DEFAULT false, 13 | OUT slot_name name, OUT lsn pg_lsn) 14 | RETURNS RECORD 15 | LANGUAGE C 16 | STRICT VOLATILE 17 | AS 'MODULE_PATHNAME', 'pg_create_logical_replication_slot_lsn'; 18 | 19 | -- NOTE: pg_create_logical_replication_slot_lsn() checks permissions internally, no need to worry here -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2022, PostgreSQL Global Development Group 2 | 3 | Permission to use, copy, modify, and distribute this software and its 4 | documentation for any purpose, without fee, and without a written agreement 5 | is hereby granted, provided that the above copyright notice and this 6 | paragraph and the following two paragraphs appear in all copies. 7 | 8 | IN NO EVENT SHALL POSTGRESQL GLOBAL DEVELOPMENT GROUP BE LIABLE TO ANY 9 | PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, 10 | INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 11 | DOCUMENTATION, EVEN IF POSTGRESQL GLOBAL DEVELOPMENT GROUP HAS BEEN 12 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | 14 | POSTGRESQL GLOBAL DEVELOPMENT GROUP SPECIFICALLY DISCLAIMS ANY WARRANTIES, 15 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 16 | AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 17 | ON AN "AS IS" BASIS, AND POSTGRESQL GLOBAL DEVELOPMENT GROUP HAS NO 18 | OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 19 | MODIFICATIONS. 20 | -------------------------------------------------------------------------------- /pg_tm_aux--1.1--1.1.1.sql: -------------------------------------------------------------------------------- 1 | /* contrib/pg_tm_aux/pg_tm_aux--1.0--1.1.sql */ 2 | 3 | -- complain if script is sourced in psql, rather than via CREATE EXTENSION 4 | \echo Use "CREATE EXTENSION pg_tm_aux" to load this file. \quit 5 | 6 | DROP FUNCTION pg_create_logical_replication_slot_lsn( 7 | IN slot_name name, IN plugin name, 8 | IN temporary boolean, IN restart_lsn pg_lsn, 9 | OUT slot_name name, OUT lsn pg_lsn); 10 | 11 | DROP FUNCTION pg_create_logical_replication_slot_lsn( 12 | IN slot_name name, IN plugin name, 13 | IN temporary boolean, IN restart_lsn pg_lsn, 14 | IN force boolean, 15 | OUT slot_name name, OUT lsn pg_lsn); 16 | -- 17 | -- pg_create_logical_replication_slot_lsn() 18 | -- 19 | CREATE OR REPLACE FUNCTION pg_create_logical_replication_slot_lsn( 20 | IN slot_name name, IN plugin name, 21 | IN temporary boolean DEFAULT false, IN restart_lsn pg_lsn DEFAULT null, 22 | IN force boolean DEFAULT true, 23 | OUT slot_name name, OUT lsn pg_lsn) 24 | RETURNS RECORD 25 | LANGUAGE C 26 | STRICT VOLATILE 27 | AS 'MODULE_PATHNAME', 'pg_create_logical_replication_slot_lsn'; 28 | 29 | -- NOTE: pg_create_logical_replication_slot_lsn() checks permissions internally, no need to worry here 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pg_tm_aux 2 | **(Not needed since Postgres 17, use logical slots failover)** 3 | 4 | Extension to create a logical replication slot in the past. It is useful to implement continuous logical streaming from the highly available cluster on physical replication. When primary node of a cluster is failovered, we need to start logical streaming from new node. 5 | 6 | We cannot start logical replication from LSN different from LSN of a slot. And cannot create a slot on LSN in the past, particularly before or right after promotion. 7 | 8 | This leads to massive waste of network bandwidth in our installations, due to necessity of initial table sync. 9 | 10 | This extension implements Yandex Data Transfer auxiliary functions to create slot in the past. 11 | 12 | ## Usage 13 | ``` 14 | SELECT * from pg_create_logical_replication_slot_lsn('dtt3gjq2tfmocenb6vru', 'wal2json', false, pg_lsn('1/20030948')); 15 | SELECT * from pg_logical_slot_peek_changes('dtt3gjq2tfmocenb6vru', null, null); 16 | ``` 17 | ## Limitations 18 | 19 | In certain cases pg_tm_aux cannot create slot: 20 | 1. WAL for LSN is not accesible anymore on the new primary server 21 | 2. Catalog snapshot cannot be built for LSN 22 | 23 | Logical replication may be slightly ahead of physical replication that is acknowledged by synchronous\quorum replics. In this case, logical stream might have some transactions that are not committed and will not survive Primary node failover. To protect from this pg_tm_aux do not allow to create a slot on current timeline. For more informatin please refer to [FOSDEM talk Caveats of replication](https://archive.fosdem.org/2021/schedule/event/postgresql_caveats_of_replication/). 24 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: pg-tm-aux 2 | Section: database 3 | Priority: optional 4 | Maintainer: Andrey Borodin (Yandex.Cloud) 5 | Build-Depends: debhelper (>= 9), postgresql-server-dev-all (>= 195~), git 6 | Standards-Version: 1.1-0 7 | Homepage: https://github.com/x4m/pg_tm_aux 8 | 9 | Package: pg-tm-aux-10 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-10 12 | Description: Extension to create a logical replication slot in the past. 13 | 14 | Package: pg-tm-aux-11 15 | Architecture: any 16 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-11 17 | Description: Extension to create a logical replication slot in the past. 18 | 19 | Package: pg-tm-aux-12 20 | Architecture: any 21 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-12 22 | Description: Extension to create a logical replication slot in the past. 23 | 24 | Package: pg-tm-aux-13 25 | Architecture: any 26 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-13 27 | Description: Extension to create a logical replication slot in the past. 28 | 29 | Package: pg-tm-aux-14 30 | Architecture: any 31 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-14 32 | Description: Extension to create a logical replication slot in the past. 33 | 34 | Package: pg-tm-aux-15 35 | Architecture: any 36 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-15 37 | Description: Extension to create a logical replication slot in the past. 38 | 39 | Package: pg-tm-aux-16 40 | Architecture: any 41 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-16 42 | Description: Extension to create a logical replication slot in the past. 43 | 44 | Package: pg-tm-aux-17 45 | Architecture: any 46 | Depends: ${shlibs:Depends}, ${misc:Depends}, postgresql-17 47 | Description: Extension to create a logical replication slot in the past. 48 | -------------------------------------------------------------------------------- /pg_tm_aux.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | * 3 | * pg_tm_aux.c 4 | * Transfer manager auxilary functions 5 | * 6 | *------------------------------------------------------------------------- 7 | */ 8 | #include "postgres.h" 9 | 10 | #include 11 | 12 | #include "access/htup_details.h" 13 | #include "access/timeline.h" 14 | #include "access/xlog_internal.h" 15 | #include "access/xlogutils.h" 16 | #include "funcapi.h" 17 | #include "miscadmin.h" 18 | #include "replication/decode.h" 19 | #include "replication/logical.h" 20 | #include "replication/slot.h" 21 | #include "utils/acl.h" 22 | #include "utils/builtins.h" 23 | #include "utils/inval.h" 24 | #include "utils/pg_lsn.h" 25 | #include "utils/resowner.h" 26 | 27 | #if (PG_VERSION_NUM < 130000) 28 | #include "replication/logicalfuncs.h" 29 | #endif 30 | 31 | 32 | PG_MODULE_MAGIC; 33 | 34 | static void 35 | check_permissions(void) 36 | { 37 | if (!superuser() && !has_rolreplication(GetUserId())) 38 | { 39 | Oid role = get_role_oid("mdb_replication", true); 40 | if (is_member_of_role(GetUserId(), role)) 41 | return; 42 | ereport(ERROR, 43 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), 44 | errmsg("must be superuser or replication role to use replication slots"))); 45 | } 46 | } 47 | 48 | static void check_lsn_not_on_current_timeline(XLogRecPtr target_lsn) 49 | { 50 | char path[MAXPGPATH]; 51 | int sendFile = -1; 52 | XLogSegNo sendSegNo = 0; 53 | #if (PG_VERSION_NUM >= 150000) 54 | TimeLineID ThisTimeLineID = GetWALInsertionTimeLine(); 55 | #endif 56 | List *timelineHistory = readTimeLineHistory(ThisTimeLineID); 57 | TimeLineID target_tli = tliOfPointInHistory(target_lsn, timelineHistory); 58 | list_free_deep(timelineHistory); 59 | 60 | if (target_tli == ThisTimeLineID) 61 | elog(ERROR, "This timeline %u includes slot LSN %X/%X. The slot must be created before switchover.", 62 | ThisTimeLineID, 63 | (uint32) (target_lsn >> 32), 64 | (uint32) (target_lsn)); 65 | 66 | XLByteToSeg(target_lsn, sendSegNo 67 | #if (PG_VERSION_NUM >= 110000) 68 | ,wal_segment_size 69 | #endif 70 | ); 71 | 72 | XLogFilePath(path, target_tli, sendSegNo 73 | #if (PG_VERSION_NUM >= 110000) 74 | ,wal_segment_size 75 | #endif 76 | ); 77 | 78 | sendFile = BasicOpenFile(path, O_RDONLY | PG_BINARY 79 | #if (PG_VERSION_NUM < 110000) 80 | , 0 81 | #endif 82 | ); 83 | 84 | if (sendFile < 0) 85 | { 86 | if (errno == ENOENT) 87 | ereport(ERROR, 88 | (errcode_for_file_access(), 89 | errmsg("requested WAL segment %s has already been removed", 90 | path))); 91 | else 92 | ereport(ERROR, 93 | (errcode_for_file_access(), 94 | errmsg("could not open file \"%s\": %m", 95 | path))); 96 | } 97 | else 98 | { 99 | close(sendFile); 100 | } 101 | } 102 | 103 | // We have to hack CreateInitDecodingContext() when it was without restart_lsn 104 | #if PG_VERSION_NUM < 120000 105 | 106 | #include "utils/memutils.h" 107 | #include "storage/procarray.h" 108 | #include "access/xact.h" 109 | /* 110 | * Create a new decoding context, for a new logical slot. 111 | * 112 | * plugin contains the name of the output plugin 113 | * output_plugin_options contains options passed to the output plugin 114 | * read_page, prepare_write, do_write, update_progress 115 | * callbacks that have to be filled to perform the use-case dependent, 116 | * actual, work. 117 | * 118 | * Needs to be called while in a memory context that's at least as long lived 119 | * as the decoding context because further memory contexts will be created 120 | * inside it. 121 | * 122 | * Returns an initialized decoding context after calling the output plugin's 123 | * startup function. 124 | */ 125 | static LogicalDecodingContext * 126 | CreateInitDecodingContextExt(char *plugin, 127 | List *output_plugin_options, 128 | bool need_full_snapshot, 129 | XLogPageReadCB read_page, 130 | LogicalOutputPluginWriterPrepareWrite prepare_write, 131 | LogicalOutputPluginWriterWrite do_write, 132 | LogicalOutputPluginWriterUpdateProgress update_progress, 133 | XLogRecPtr restart_lsn) 134 | { 135 | TransactionId xmin_horizon = InvalidTransactionId; 136 | ReplicationSlot *slot; 137 | 138 | /* shorter lines... */ 139 | slot = MyReplicationSlot; 140 | 141 | /* first some sanity checks that are unlikely to be violated */ 142 | if (slot == NULL) 143 | elog(ERROR, "cannot perform logical decoding without an acquired slot"); 144 | 145 | if (plugin == NULL) 146 | elog(ERROR, "cannot initialize logical decoding without a specified plugin"); 147 | 148 | /* Make sure the passed slot is suitable. These are user facing errors. */ 149 | if (SlotIsPhysical(slot)) 150 | ereport(ERROR, 151 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), 152 | errmsg("cannot use physical replication slot for logical decoding"))); 153 | 154 | if (slot->data.database != MyDatabaseId) 155 | ereport(ERROR, 156 | (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), 157 | errmsg("replication slot \"%s\" was not created in this database", 158 | NameStr(slot->data.name)))); 159 | 160 | if (IsTransactionState() && 161 | GetTopTransactionIdIfAny() != InvalidTransactionId) 162 | ereport(ERROR, 163 | (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), 164 | errmsg("cannot create logical replication slot in transaction that has performed writes"))); 165 | 166 | /* register output plugin name with slot */ 167 | SpinLockAcquire(&slot->mutex); 168 | StrNCpy(NameStr(slot->data.plugin), plugin, NAMEDATALEN); 169 | SpinLockRelease(&slot->mutex); 170 | 171 | if (XLogRecPtrIsInvalid(restart_lsn)) 172 | ReplicationSlotReserveWal(); 173 | else 174 | { 175 | SpinLockAcquire(&slot->mutex); 176 | slot->data.restart_lsn = restart_lsn; 177 | SpinLockRelease(&slot->mutex); 178 | } 179 | 180 | /* ---- 181 | * This is a bit tricky: We need to determine a safe xmin horizon to start 182 | * decoding from, to avoid starting from a running xacts record referring 183 | * to xids whose rows have been vacuumed or pruned 184 | * already. GetOldestSafeDecodingTransactionId() returns such a value, but 185 | * without further interlock its return value might immediately be out of 186 | * date. 187 | * 188 | * So we have to acquire the ProcArrayLock to prevent computation of new 189 | * xmin horizons by other backends, get the safe decoding xid, and inform 190 | * the slot machinery about the new limit. Once that's done the 191 | * ProcArrayLock can be released as the slot machinery now is 192 | * protecting against vacuum. 193 | * 194 | * Note that, temporarily, the data, not just the catalog, xmin has to be 195 | * reserved if a data snapshot is to be exported. Otherwise the initial 196 | * data snapshot created here is not guaranteed to be valid. After that 197 | * the data xmin doesn't need to be managed anymore and the global xmin 198 | * should be recomputed. As we are fine with losing the pegged data xmin 199 | * after crash - no chance a snapshot would get exported anymore - we can 200 | * get away with just setting the slot's 201 | * effective_xmin. ReplicationSlotRelease will reset it again. 202 | * 203 | * ---- 204 | */ 205 | LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); 206 | 207 | xmin_horizon = GetOldestSafeDecodingTransactionId(!need_full_snapshot); 208 | 209 | slot->effective_catalog_xmin = xmin_horizon; 210 | slot->data.catalog_xmin = xmin_horizon; 211 | if (need_full_snapshot) 212 | slot->effective_xmin = xmin_horizon; 213 | 214 | ReplicationSlotsComputeRequiredXmin(true); 215 | 216 | LWLockRelease(ProcArrayLock); 217 | 218 | ReplicationSlotMarkDirty(); 219 | ReplicationSlotSave(); 220 | 221 | return NULL; 222 | } 223 | #endif 224 | 225 | /* 226 | * Helper function for creating a new logical replication slot with 227 | * given arguments. Note that this function doesn't release the created 228 | * slot. 229 | * 230 | * When find_startpoint is false, the slot's confirmed_flush is not set; it's 231 | * caller's responsibility to ensure it's set to something sensible. 232 | */ 233 | static void 234 | create_logical_replication_slot(char *name, char *plugin, 235 | bool temporary, XLogRecPtr restart_lsn) 236 | { 237 | LogicalDecodingContext *ctx = NULL; 238 | Assert(!MyReplicationSlot); 239 | 240 | /* 241 | * Acquire a logical decoding slot, this will check for conflicting names. 242 | * Initially create persistent slot as ephemeral - that allows us to 243 | * nicely handle errors during initialization because it'll get dropped if 244 | * this transaction fails. We'll make it persistent at the end. Temporary 245 | * slots can be created as temporary from beginning as they get dropped on 246 | * error as well. 247 | */ 248 | #if (PG_VERSION_NUM >= 170000) 249 | ReplicationSlotCreate(name, true, 250 | temporary ? RS_TEMPORARY : RS_EPHEMERAL, false, false, false); 251 | #else 252 | # if (PG_VERSION_NUM >= 140000) 253 | ReplicationSlotCreate(name, true, 254 | temporary ? RS_TEMPORARY : RS_EPHEMERAL, false); 255 | # else 256 | ReplicationSlotCreate(name, true, 257 | temporary ? RS_TEMPORARY : RS_EPHEMERAL); 258 | # endif 259 | #endif 260 | 261 | /* We intentionaly ignore values found by create_logical_replication_slot */ 262 | /* This actually moves slot backwards and constitues race condition */ 263 | SpinLockAcquire(&MyReplicationSlot->mutex); 264 | MyReplicationSlot->data.restart_lsn = restart_lsn; 265 | MyReplicationSlot->data.confirmed_flush = restart_lsn; 266 | SpinLockRelease(&MyReplicationSlot->mutex); 267 | 268 | 269 | /* 270 | * Create logical decoding context to find start point or, if we don't 271 | * need it, to 1) bump slot's restart_lsn and xmin 2) check plugin sanity. 272 | * 273 | * Note: when !find_startpoint this is still important, because it's at 274 | * this point that the output plugin is validated. 275 | */ 276 | #if (PG_VERSION_NUM >= 130000) 277 | ctx = CreateInitDecodingContext(plugin, NIL, 278 | false, /* just catalogs is OK */ 279 | restart_lsn, 280 | XL_ROUTINE(.page_read = read_local_xlog_page, 281 | .segment_open = wal_segment_open, 282 | .segment_close = wal_segment_close), 283 | NULL, NULL, NULL); 284 | #elif (PG_VERSION_NUM >= 120000) 285 | ctx = CreateInitDecodingContext(plugin, NIL, 286 | false, /* just catalogs is OK */ 287 | restart_lsn, 288 | logical_read_local_xlog_page, NULL, NULL, 289 | NULL); 290 | #else 291 | ctx = CreateInitDecodingContextExt(plugin, NIL, 292 | false, /* do not build snapshot */ 293 | logical_read_local_xlog_page, NULL, NULL, 294 | NULL, restart_lsn); 295 | #endif 296 | 297 | /* don't need the decoding context anymore */ 298 | if (ctx != NULL) 299 | FreeDecodingContext(ctx); 300 | } 301 | 302 | PG_FUNCTION_INFO_V1(pg_create_logical_replication_slot_lsn); 303 | 304 | /* 305 | * SQL function for creating a new logical replication slot for a given LSN. 306 | */ 307 | Datum 308 | pg_create_logical_replication_slot_lsn(PG_FUNCTION_ARGS) 309 | { 310 | Name name = PG_GETARG_NAME(0); 311 | Name plugin = PG_GETARG_NAME(1); 312 | bool temporary = PG_GETARG_BOOL(2); 313 | XLogRecPtr restart_lsn = PG_GETARG_LSN(3); 314 | bool force = false; 315 | Datum result; 316 | TupleDesc tupdesc; 317 | HeapTuple tuple; 318 | Datum values[2]; 319 | bool nulls[2]; 320 | 321 | if (PG_NARGS() >= 5) 322 | force = PG_GETARG_BOOL(4); 323 | 324 | if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) 325 | elog(ERROR, "return type must be a row type"); 326 | 327 | check_permissions(); 328 | 329 | if (!force) 330 | check_lsn_not_on_current_timeline(restart_lsn); 331 | 332 | CheckLogicalDecodingRequirements(); 333 | 334 | create_logical_replication_slot(NameStr(*name), 335 | NameStr(*plugin), 336 | temporary, 337 | restart_lsn); 338 | 339 | values[0] = NameGetDatum(&MyReplicationSlot->data.name); 340 | values[1] = LSNGetDatum(MyReplicationSlot->data.confirmed_flush); 341 | 342 | memset(nulls, 0, sizeof(nulls)); 343 | 344 | tuple = heap_form_tuple(tupdesc, values, nulls); 345 | result = HeapTupleGetDatum(tuple); 346 | 347 | /* ok, slot is now fully created, mark it as persistent if needed */ 348 | if (!temporary) 349 | ReplicationSlotPersist(); 350 | ReplicationSlotRelease(); 351 | 352 | PG_RETURN_DATUM(result); 353 | } 354 | --------------------------------------------------------------------------------