PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgrepack.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pgrepack.c
4 * Logical Replication output plugin for REPACK command
5 *
6 * Copyright (c) 2026, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * src/backend/replication/pgrepack/pgrepack.c
10 *
11 *-------------------------------------------------------------------------
12 */
13#include "postgres.h"
14
15#include "access/detoast.h"
16#include "commands/repack.h"
19#include "utils/memutils.h"
20
22 .name = "pgrepack",
23 .version = PG_VERSION
24);
25
27 OutputPluginOptions *opt, bool is_init);
30 ReorderBufferTXN *txn);
32 ReorderBufferTXN *txn, XLogRecPtr commit_lsn);
34 Relation relation, ReorderBufferChange *change);
35static void repack_store_change(LogicalDecodingContext *ctx, Relation relation,
37
38void
47
48
49/* initialize this plugin */
50static void
52 bool is_init)
53{
55
56 if (!AmRepackWorker())
59 errmsg("unsupported use of logical decoding plugin \"%s\"",
60 "pgrepack"),
61 errdetail("This plugin can only be used by %s.",
62 "REPACK (CONCURRENTLY)"));
63
64 /* Initial setup of our private state */
67 dstate->change_cxt = AllocSetContextCreate(ctx->context,
68 "REPACK - change",
70 /* repack_setup_logical_decoding fills in the rest */
72
73 /* Probably unnecessary, as we don't use the SQL interface ... */
75
76 if (ctx->output_plugin_options != NIL)
77 {
80 errmsg("this plugin does not expect any options"));
81 }
82}
83
84static void
88
89/*
90 * As we don't release the slot during processing of particular table, there's
91 * no room for SQL interface, even for debugging purposes. Therefore we need
92 * neither OutputPluginPrepareWrite() nor OutputPluginWrite() in the plugin
93 * callbacks. (Although we might want to write custom callbacks, this API
94 * seems to be unnecessarily generic for our purposes.)
95 */
96
97/* BEGIN callback */
98static void
102
103/* COMMIT callback */
104static void
109
110/*
111 * Callback for individual changed tuples
112 */
113static void
115 Relation relation, ReorderBufferChange *change)
116{
119
120 /* Changes of other relation should not have been decoded. */
121 Assert(RelationGetRelid(relation) == private->relid);
122
123 /* Decode entry depending on its type */
124 switch (change->action)
125 {
127 {
128 HeapTuple newtuple;
129
130 newtuple = change->data.tp.newtuple;
131
132 /*
133 * Identity checks in the main function should have made this
134 * impossible.
135 */
136 if (newtuple == NULL)
137 elog(ERROR, "incomplete insert info");
138
139 repack_store_change(ctx, relation, CHANGE_INSERT, newtuple);
140 }
141 break;
143 {
144 HeapTuple oldtuple,
145 newtuple;
146
147 oldtuple = change->data.tp.oldtuple;
148 newtuple = change->data.tp.newtuple;
149
150 if (newtuple == NULL)
151 elog(ERROR, "incomplete update info");
152
153 if (oldtuple != NULL)
154 repack_store_change(ctx, relation, CHANGE_UPDATE_OLD, oldtuple);
155
156 repack_store_change(ctx, relation, CHANGE_UPDATE_NEW, newtuple);
157 }
158 break;
160 {
161 HeapTuple oldtuple;
162
163 oldtuple = change->data.tp.oldtuple;
164
165 if (oldtuple == NULL)
166 elog(ERROR, "incomplete delete info");
167
168 repack_store_change(ctx, relation, CHANGE_DELETE, oldtuple);
169 }
170 break;
171 default:
172
173 /*
174 * Should not come here. This includes TRUNCATE of the table being
175 * processed. heap_decode() cannot check the file locator easily,
176 * but we assume that TRUNCATE uses AccessExclusiveLock on the
177 * table so it should not occur during REPACK (CONCURRENTLY).
178 */
179 Assert(false);
180 break;
181 }
182}
183
184/*
185 * Write the given tuple, with the given change kind, to the repack spill
186 * file. Later, the repack decoding worker can read these and replay
187 * the operations on the new copy of the table.
188 *
189 * For each change affecting the table being repacked, we store enough
190 * information about each tuple in it, so that it can be replayed in the
191 * new copy of the table.
192 */
193static void
196{
199 BufFile *file;
200 List *attrs_ext = NIL;
201 int natt_ext;
202
204 file = dstate->file;
205
206 /* Store the change kind. */
207 BufFileWrite(file, &kind, 1);
208
209 /* Use a frequently-reset context to avoid dealing with leaks manually */
210 oldcxt = MemoryContextSwitchTo(dstate->change_cxt);
211
212 /*
213 * If the tuple contains "external indirect" attributes, we need to write
214 * the contents to the file because we have no control over that memory.
215 */
216 if (HeapTupleHasExternal(tuple))
217 {
218 TupleDesc desc = RelationGetDescr(relation);
219 TupleTableSlot *slot;
220
221 /* Initialize the slot, if not done already */
222 if (dstate->slot == NULL)
223 {
225
226 MemoryContextSwitchTo(dstate->worker_cxt);
228 CurrentResourceOwner = dstate->worker_resowner;
230 MemoryContextSwitchTo(dstate->change_cxt);
232 }
233
234 slot = dstate->slot;
235 ExecStoreHeapTuple(tuple, slot, false);
236
237 /*
238 * Loop over all attributes, and find out which ones we need to spill
239 * separately, to wit: each one that's a non-null varlena and stored
240 * out of line.
241 */
242 for (int i = 0; i < desc->natts; i++)
243 {
246
247 if (attr->attisdropped || attr->attlen != -1 ||
248 slot_attisnull(slot, i + 1))
249 continue;
250
251 slot_getsomeattrs(slot, i + 1);
252
253 /*
254 * This is a non-null varlena datum, but we only care if it's
255 * out-of-line
256 */
259 continue;
260
261 /*
262 * We spill any indirect-external attributes separately from the
263 * heap tuple. Anything else is written as is.
264 */
267 else
268 {
269 /*
270 * Logical decoding should not produce "external expanded"
271 * attributes (those actually should never appear on disk), so
272 * only TOASTed attribute can be seen here.
273 *
274 * We get here if the table has external values but only
275 * in-line values are being updated now.
276 */
278 }
279 }
280
281 ExecClearTuple(slot);
282 }
283
284 /*
285 * First, write the original heap tuple, prefixed by its length. Note
286 * that the external-toast tag for each toasted attribute will be present
287 * in what we write, so that we know where to restore each one later.
288 */
289 BufFileWrite(file, &tuple->t_len, sizeof(tuple->t_len));
290 BufFileWrite(file, tuple->t_data, tuple->t_len);
291
292 /* Then, write the number of external attributes we found. */
294 BufFileWrite(file, &natt_ext, sizeof(natt_ext));
295
296 /* Finally, the attributes themselves, if any */
298 {
301 /* These attributes could be large, so free them right away */
303 }
304
305 /* Cleanup. */
307 MemoryContextReset(dstate->change_cxt);
308}
void BufFileWrite(BufFile *file, const void *ptr, size_t size)
Definition buffile.c:681
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308
#define Assert(condition)
Definition c.h:1002
varlena * detoast_external_attr(varlena *attr)
Definition detoast.c:45
int errcode(int sqlerrcode)
Definition elog.c:875
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsHeapTuple
Definition execTuples.c:85
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
#define palloc0_object(type)
Definition fe_memutils.h:90
#define PG_MODULE_MAGIC_EXT(...)
Definition fmgr.h:540
static bool HeapTupleHasExternal(const HeapTupleData *tuple)
int i
Definition isn.c:77
List * lappend(List *list, void *datum)
Definition list.c:339
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
void pfree(void *pointer)
Definition mcxt.c:1619
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
static char * errmsg
@ OUTPUT_PLUGIN_BINARY_OUTPUT
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
static void repack_commit_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, XLogRecPtr commit_lsn)
Definition pgrepack.c:105
static void repack_shutdown(LogicalDecodingContext *ctx)
Definition pgrepack.c:85
static void repack_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt, bool is_init)
Definition pgrepack.c:51
static void repack_process_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn, Relation relation, ReorderBufferChange *change)
Definition pgrepack.c:114
static void repack_store_change(LogicalDecodingContext *ctx, Relation relation, ConcurrentChangeKind kind, HeapTuple tuple)
Definition pgrepack.c:194
static void repack_begin_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn)
Definition pgrepack.c:99
void _PG_output_plugin_init(OutputPluginCallbacks *cb)
Definition pgrepack.c:39
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static int fb(int x)
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetDescr(relation)
Definition rel.h:542
@ REORDER_BUFFER_CHANGE_INSERT
@ REORDER_BUFFER_CHANGE_DELETE
@ REORDER_BUFFER_CHANGE_UPDATE
#define CHANGE_UPDATE_OLD
#define CHANGE_DELETE
#define CHANGE_UPDATE_NEW
char ConcurrentChangeKind
#define CHANGE_INSERT
bool AmRepackWorker(void)
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
bool attisdropped
Definition tupdesc.h:78
uint32 t_len
Definition htup.h:64
HeapTupleHeader t_data
Definition htup.h:68
Definition pg_list.h:54
MemoryContext context
Definition logical.h:36
void * output_writer_private
Definition logical.h:81
List * output_plugin_options
Definition logical.h:59
LogicalDecodeStartupCB startup_cb
LogicalDecodeCommitCB commit_cb
LogicalDecodeBeginCB begin_cb
LogicalDecodeChangeCB change_cb
LogicalDecodeShutdownCB shutdown_cb
OutputPluginOutputType output_type
ReorderBufferChangeType action
struct ReorderBufferChange::@118::@119 tp
union ReorderBufferChange::@118 data
Datum * tts_values
Definition tuptable.h:131
Definition c.h:835
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
static void slot_getsomeattrs(TupleTableSlot *slot, int attnum)
Definition tuptable.h:376
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition tuptable.h:403
static bool VARATT_IS_EXTERNAL_ONDISK(const void *PTR)
Definition varatt.h:361
static Size VARSIZE_ANY(const void *PTR)
Definition varatt.h:460
static bool VARATT_IS_EXTERNAL(const void *PTR)
Definition varatt.h:354
static bool VARATT_IS_EXTERNAL_INDIRECT(const void *PTR)
Definition varatt.h:368
const char * name
uint64 XLogRecPtr
Definition xlogdefs.h:21