1
/*-------------------------------------------------------------------------
4
* public interface routines to storage manager switch.
6
* All file system operations in POSTGRES dispatch through these
9
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
10
* Portions Copyright (c) 1994, Regents of the University of California
14
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.85 2005-01-10 20:02:22 tgl Exp $
16
*-------------------------------------------------------------------------
20
#include "access/xact.h"
21
#include "commands/tablespace.h"
22
#include "storage/bufmgr.h"
23
#include "storage/freespace.h"
24
#include "storage/ipc.h"
25
#include "storage/smgr.h"
26
#include "utils/hsearch.h"
27
#include "utils/memutils.h"
31
* This struct of function pointers defines the API between smgr.c and
32
* any individual storage manager module. Note that smgr subfunctions are
33
* generally expected to return TRUE on success, FALSE on error. (For
34
* nblocks and truncate we instead say that returning InvalidBlockNumber
35
* indicates an error.)
39
bool (*smgr_init) (void); /* may be NULL */
40
bool (*smgr_shutdown) (void); /* may be NULL */
41
bool (*smgr_close) (SMgrRelation reln);
42
bool (*smgr_create) (SMgrRelation reln, bool isRedo);
43
bool (*smgr_unlink) (RelFileNode rnode, bool isRedo);
44
bool (*smgr_extend) (SMgrRelation reln, BlockNumber blocknum,
45
char *buffer, bool isTemp);
46
bool (*smgr_read) (SMgrRelation reln, BlockNumber blocknum,
48
bool (*smgr_write) (SMgrRelation reln, BlockNumber blocknum,
49
char *buffer, bool isTemp);
50
BlockNumber (*smgr_nblocks) (SMgrRelation reln);
51
BlockNumber (*smgr_truncate) (SMgrRelation reln, BlockNumber nblocks,
53
bool (*smgr_immedsync) (SMgrRelation reln);
54
bool (*smgr_commit) (void); /* may be NULL */
55
bool (*smgr_abort) (void); /* may be NULL */
56
bool (*smgr_sync) (void); /* may be NULL */
60
static const f_smgr smgrsw[] = {
62
{mdinit, NULL, mdclose, mdcreate, mdunlink, mdextend,
63
mdread, mdwrite, mdnblocks, mdtruncate, mdimmedsync,
68
static const int NSmgr = lengthof(smgrsw);
72
* Each backend has a hashtable that stores all extant SMgrRelation objects.
74
static HTAB *SMgrRelationHash = NULL;
77
* We keep a list of all relations (represented as RelFileNode values)
78
* that have been created or deleted in the current transaction. When
79
* a relation is created, we create the physical file immediately, but
80
* remember it so that we can delete the file again if the current
81
* transaction is aborted. Conversely, a deletion request is NOT
82
* executed immediately, but is just entered in the list. When and if
83
* the transaction commits, we can delete the physical file.
85
* To handle subtransactions, every entry is marked with its transaction
86
* nesting level. At subtransaction commit, we reassign the subtransaction's
87
* entries to the parent nesting level. At subtransaction abort, we can
88
* immediately execute the abort-time actions for all entries of the current
91
* NOTE: the list is kept in TopMemoryContext to be sure it won't disappear
92
* unbetimes. It'd probably be OK to keep it in TopTransactionContext,
93
* but I'm being paranoid.
96
typedef struct PendingRelDelete
98
RelFileNode relnode; /* relation that may need to be deleted */
99
int which; /* which storage manager? */
100
bool isTemp; /* is it a temporary relation? */
101
bool atCommit; /* T=delete at commit; F=delete at abort */
102
int nestLevel; /* xact nesting level of request */
103
struct PendingRelDelete *next; /* linked-list link */
106
static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
110
* Declarations for smgr-related XLOG records
112
* Note: we log file creation and truncation here, but logging of deletion
113
* actions is handled by xact.c, because it is part of transaction commit.
116
/* XLOG gives us high 4 bits */
117
#define XLOG_SMGR_CREATE 0x10
118
#define XLOG_SMGR_TRUNCATE 0x20
120
typedef struct xl_smgr_create
125
typedef struct xl_smgr_truncate
132
/* local function prototypes */
133
static void smgrshutdown(int code, Datum arg);
134
static void smgr_internal_unlink(RelFileNode rnode, int which,
135
bool isTemp, bool isRedo);
139
* smgrinit(), smgrshutdown() -- Initialize or shut down all storage
142
* Note: in the normal multiprocess scenario with a postmaster, these are
143
* called at postmaster start and stop, not per-backend.
150
for (i = 0; i < NSmgr; i++)
152
if (smgrsw[i].smgr_init)
154
if (!(*(smgrsw[i].smgr_init)) ())
155
elog(FATAL, "smgr initialization failed on %s: %m",
156
DatumGetCString(DirectFunctionCall1(smgrout,
161
/* register the shutdown proc */
162
on_proc_exit(smgrshutdown, 0);
166
smgrshutdown(int code, Datum arg)
170
for (i = 0; i < NSmgr; i++)
172
if (smgrsw[i].smgr_shutdown)
174
if (!(*(smgrsw[i].smgr_shutdown)) ())
175
elog(FATAL, "smgr shutdown failed on %s: %m",
176
DatumGetCString(DirectFunctionCall1(smgrout,
183
* smgropen() -- Return an SMgrRelation object, creating it if need be.
185
* This does not attempt to actually open the object.
188
smgropen(RelFileNode rnode)
193
if (SMgrRelationHash == NULL)
195
/* First time through: initialize the hash table */
198
MemSet(&ctl, 0, sizeof(ctl));
199
ctl.keysize = sizeof(RelFileNode);
200
ctl.entrysize = sizeof(SMgrRelationData);
202
SMgrRelationHash = hash_create("smgr relation table", 400,
203
&ctl, HASH_ELEM | HASH_FUNCTION);
206
/* Look up or create an entry */
207
reln = (SMgrRelation) hash_search(SMgrRelationHash,
212
(errcode(ERRCODE_OUT_OF_MEMORY),
213
errmsg("out of memory")));
215
/* Initialize it if not present before */
218
/* hash_search already filled in the lookup key */
219
reln->smgr_owner = NULL;
220
reln->smgr_which = 0; /* we only have md.c at present */
221
reln->md_fd = NULL; /* mark it not open */
228
* smgrsetowner() -- Establish a long-lived reference to an SMgrRelation object
230
* There can be only one owner at a time; this is sufficient since currently
231
* the only such owners exist in the relcache.
234
smgrsetowner(SMgrRelation *owner, SMgrRelation reln)
237
* First, unhook any old owner. (Normally there shouldn't be any, but
238
* it seems possible that this can happen during swap_relation_files()
239
* depending on the order of processing. It's ok to close the old
240
* relcache entry early in that case.)
242
if (reln->smgr_owner)
243
*(reln->smgr_owner) = NULL;
245
/* Now establish the ownership relationship. */
246
reln->smgr_owner = owner;
251
* smgrclose() -- Close and delete an SMgrRelation object.
254
smgrclose(SMgrRelation reln)
258
if (!(*(smgrsw[reln->smgr_which].smgr_close)) (reln))
260
(errcode_for_file_access(),
261
errmsg("could not close relation %u/%u/%u: %m",
262
reln->smgr_rnode.spcNode,
263
reln->smgr_rnode.dbNode,
264
reln->smgr_rnode.relNode)));
266
owner = reln->smgr_owner;
268
if (hash_search(SMgrRelationHash,
269
(void *) &(reln->smgr_rnode),
270
HASH_REMOVE, NULL) == NULL)
271
elog(ERROR, "SMgrRelation hashtable corrupted");
274
* Unhook the owner pointer, if any. We do this last since in the
275
* remote possibility of failure above, the SMgrRelation object will still
283
* smgrcloseall() -- Close all existing SMgrRelation objects.
288
HASH_SEQ_STATUS status;
291
/* Nothing to do if hashtable not set up */
292
if (SMgrRelationHash == NULL)
295
hash_seq_init(&status, SMgrRelationHash);
297
while ((reln = (SMgrRelation) hash_seq_search(&status)) != NULL)
302
* smgrclosenode() -- Close SMgrRelation object for given RelFileNode,
305
* This has the same effects as smgrclose(smgropen(rnode)), but it avoids
306
* uselessly creating a hashtable entry only to drop it again when no
307
* such entry exists already.
310
smgrclosenode(RelFileNode rnode)
314
/* Nothing to do if hashtable not set up */
315
if (SMgrRelationHash == NULL)
318
reln = (SMgrRelation) hash_search(SMgrRelationHash,
326
* smgrcreate() -- Create a new relation.
328
* Given an already-created (but presumably unused) SMgrRelation,
329
* cause the underlying disk file or other storage to be created.
331
* If isRedo is true, it is okay for the underlying file to exist
332
* already because we are in a WAL replay sequence. In this case
333
* we should make no PendingRelDelete entry; the WAL sequence will
334
* tell whether to drop the file.
337
smgrcreate(SMgrRelation reln, bool isTemp, bool isRedo)
341
xl_smgr_create xlrec;
342
PendingRelDelete *pending;
345
* We may be using the target table space for the first time in this
346
* database, so create a per-database subdirectory if needed.
348
* XXX this is a fairly ugly violation of module layering, but this seems
349
* to be the best place to put the check. Maybe
350
* TablespaceCreateDbspace should be here and not in
351
* commands/tablespace.c? But that would imply importing a lot of
352
* stuff that smgr.c oughtn't know, either.
354
TablespaceCreateDbspace(reln->smgr_rnode.spcNode,
355
reln->smgr_rnode.dbNode,
358
if (!(*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo))
360
(errcode_for_file_access(),
361
errmsg("could not create relation %u/%u/%u: %m",
362
reln->smgr_rnode.spcNode,
363
reln->smgr_rnode.dbNode,
364
reln->smgr_rnode.relNode)));
370
* Make a non-transactional XLOG entry showing the file creation. It's
371
* non-transactional because we should replay it whether the
372
* transaction commits or not; if not, the file will be dropped at
375
xlrec.rnode = reln->smgr_rnode;
377
rdata.buffer = InvalidBuffer;
378
rdata.data = (char *) &xlrec;
379
rdata.len = sizeof(xlrec);
382
lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLOG_NO_TRAN, &rdata);
384
/* Add the relation to the list of stuff to delete at abort */
385
pending = (PendingRelDelete *)
386
MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
387
pending->relnode = reln->smgr_rnode;
388
pending->which = reln->smgr_which;
389
pending->isTemp = isTemp;
390
pending->atCommit = false; /* delete if abort */
391
pending->nestLevel = GetCurrentTransactionNestLevel();
392
pending->next = pendingDeletes;
393
pendingDeletes = pending;
397
* smgrscheduleunlink() -- Schedule unlinking a relation at xact commit.
399
* The relation is marked to be removed from the store if we
400
* successfully commit the current transaction.
402
* This also implies smgrclose() on the SMgrRelation object.
405
smgrscheduleunlink(SMgrRelation reln, bool isTemp)
407
PendingRelDelete *pending;
409
/* Add the relation to the list of stuff to delete at commit */
410
pending = (PendingRelDelete *)
411
MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
412
pending->relnode = reln->smgr_rnode;
413
pending->which = reln->smgr_which;
414
pending->isTemp = isTemp;
415
pending->atCommit = true; /* delete if commit */
416
pending->nestLevel = GetCurrentTransactionNestLevel();
417
pending->next = pendingDeletes;
418
pendingDeletes = pending;
421
* NOTE: if the relation was created in this transaction, it will now
422
* be present in the pending-delete list twice, once with atCommit
423
* true and once with atCommit false. Hence, it will be physically
424
* deleted at end of xact in either case (and the other entry will be
425
* ignored by smgrDoPendingDeletes, so no error will occur). We could
426
* instead remove the existing list entry and delete the physical file
427
* immediately, but for now I'll keep the logic simple.
430
/* Now close the file and throw away the hashtable entry */
435
* smgrdounlink() -- Immediately unlink a relation.
437
* The relation is removed from the store. This should not be used
438
* during transactional operations, since it can't be undone.
440
* If isRedo is true, it is okay for the underlying file to be gone
441
* already. (In practice isRedo will always be true.)
443
* This also implies smgrclose() on the SMgrRelation object.
446
smgrdounlink(SMgrRelation reln, bool isTemp, bool isRedo)
448
RelFileNode rnode = reln->smgr_rnode;
449
int which = reln->smgr_which;
451
/* Close the file and throw away the hashtable entry */
454
smgr_internal_unlink(rnode, which, isTemp, isRedo);
458
* Shared subroutine that actually does the unlink ...
461
smgr_internal_unlink(RelFileNode rnode, int which, bool isTemp, bool isRedo)
464
* Get rid of any leftover buffers for the rel (shouldn't be any in
465
* the commit case, but there can be in the abort case).
467
DropRelFileNodeBuffers(rnode, isTemp, 0);
470
* Tell the free space map to forget this relation. It won't be
471
* accessed any more anyway, but we may as well recycle the map space
474
FreeSpaceMapForgetRel(&rnode);
477
* And delete the physical files.
479
* Note: we treat deletion failure as a WARNING, not an error, because
480
* we've already decided to commit or abort the current xact.
482
if (!(*(smgrsw[which].smgr_unlink)) (rnode, isRedo))
484
(errcode_for_file_access(),
485
errmsg("could not remove relation %u/%u/%u: %m",
492
* smgrextend() -- Add a new block to a file.
494
* The semantics are basically the same as smgrwrite(): write at the
495
* specified position. However, we are expecting to extend the
496
* relation (ie, blocknum is the current EOF), and so in case of
497
* failure we clean up by truncating.
500
smgrextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
502
if (!(*(smgrsw[reln->smgr_which].smgr_extend)) (reln, blocknum, buffer,
505
(errcode_for_file_access(),
506
errmsg("could not extend relation %u/%u/%u: %m",
507
reln->smgr_rnode.spcNode,
508
reln->smgr_rnode.dbNode,
509
reln->smgr_rnode.relNode),
510
errhint("Check free disk space.")));
514
* smgrread() -- read a particular block from a relation into the supplied
517
* This routine is called from the buffer manager in order to
518
* instantiate pages in the shared buffer cache. All storage managers
519
* return pages in the format that POSTGRES expects.
522
smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer)
524
if (!(*(smgrsw[reln->smgr_which].smgr_read)) (reln, blocknum, buffer))
526
(errcode_for_file_access(),
527
errmsg("could not read block %u of relation %u/%u/%u: %m",
529
reln->smgr_rnode.spcNode,
530
reln->smgr_rnode.dbNode,
531
reln->smgr_rnode.relNode)));
535
* smgrwrite() -- Write the supplied buffer out.
537
* This is not a synchronous write -- the block is not necessarily
538
* on disk at return, only dumped out to the kernel. However,
539
* provisions will be made to fsync the write before the next checkpoint.
541
* isTemp indicates that the relation is a temp table (ie, is managed
542
* by the local-buffer manager). In this case no provisions need be
543
* made to fsync the write before checkpointing.
546
smgrwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
548
if (!(*(smgrsw[reln->smgr_which].smgr_write)) (reln, blocknum, buffer,
551
(errcode_for_file_access(),
552
errmsg("could not write block %u of relation %u/%u/%u: %m",
554
reln->smgr_rnode.spcNode,
555
reln->smgr_rnode.dbNode,
556
reln->smgr_rnode.relNode)));
560
* smgrnblocks() -- Calculate the number of blocks in the
563
* Returns the number of blocks on success, aborts the current
564
* transaction on failure.
567
smgrnblocks(SMgrRelation reln)
571
nblocks = (*(smgrsw[reln->smgr_which].smgr_nblocks)) (reln);
574
* NOTE: if a relation ever did grow to 2^32-1 blocks, this code would
575
* fail --- but that's a good thing, because it would stop us from
576
* extending the rel another block and having a block whose number
577
* actually is InvalidBlockNumber.
579
if (nblocks == InvalidBlockNumber)
581
(errcode_for_file_access(),
582
errmsg("could not count blocks of relation %u/%u/%u: %m",
583
reln->smgr_rnode.spcNode,
584
reln->smgr_rnode.dbNode,
585
reln->smgr_rnode.relNode)));
591
* smgrtruncate() -- Truncate supplied relation to the specified number
594
* Returns the number of blocks on success, aborts the current
595
* transaction on failure.
598
smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
603
* Tell the free space map to forget anything it may have stored for
604
* the about-to-be-deleted blocks. We want to be sure it won't return
605
* bogus block numbers later on.
607
FreeSpaceMapTruncateRel(&reln->smgr_rnode, nblocks);
609
/* Do the truncation */
610
newblks = (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, nblocks,
612
if (newblks == InvalidBlockNumber)
614
(errcode_for_file_access(),
615
errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
616
reln->smgr_rnode.spcNode,
617
reln->smgr_rnode.dbNode,
618
reln->smgr_rnode.relNode,
624
* Make a non-transactional XLOG entry showing the file
625
* truncation. It's non-transactional because we should replay it
626
* whether the transaction commits or not; the underlying file
627
* change is certainly not reversible.
631
xl_smgr_truncate xlrec;
633
xlrec.blkno = newblks;
634
xlrec.rnode = reln->smgr_rnode;
636
rdata.buffer = InvalidBuffer;
637
rdata.data = (char *) &xlrec;
638
rdata.len = sizeof(xlrec);
641
lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE | XLOG_NO_TRAN,
649
* smgrimmedsync() -- Force the specified relation to stable storage.
651
* Synchronously force all of the specified relation down to disk.
653
* This is useful for building completely new relations (eg, new
654
* indexes). Instead of incrementally WAL-logging the index build
655
* steps, we can just write completed index pages to disk with smgrwrite
656
* or smgrextend, and then fsync the completed index file before
657
* committing the transaction. (This is sufficient for purposes of
658
* crash recovery, since it effectively duplicates forcing a checkpoint
659
* for the completed index. But it is *not* sufficient if one wishes
660
* to use the WAL log for PITR or replication purposes: in that case
661
* we have to make WAL entries as well.)
663
* The preceding writes should specify isTemp = true to avoid
664
* duplicative fsyncs.
667
smgrimmedsync(SMgrRelation reln)
669
if (!(*(smgrsw[reln->smgr_which].smgr_immedsync)) (reln))
671
(errcode_for_file_access(),
672
errmsg("could not sync relation %u/%u/%u: %m",
673
reln->smgr_rnode.spcNode,
674
reln->smgr_rnode.dbNode,
675
reln->smgr_rnode.relNode)));
679
* smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
681
* This also runs when aborting a subxact; we want to clean up a failed
682
* subxact immediately.
685
smgrDoPendingDeletes(bool isCommit)
687
int nestLevel = GetCurrentTransactionNestLevel();
688
PendingRelDelete *pending;
689
PendingRelDelete *prev;
690
PendingRelDelete *next;
693
for (pending = pendingDeletes; pending != NULL; pending = next)
695
next = pending->next;
696
if (pending->nestLevel < nestLevel)
698
/* outer-level entries should not be processed yet */
703
/* unlink list entry first, so we don't retry on failure */
707
pendingDeletes = next;
708
/* do deletion if called for */
709
if (pending->atCommit == isCommit)
710
smgr_internal_unlink(pending->relnode,
714
/* must explicitly free the list entry */
716
/* prev does not change */
722
* smgrGetPendingDeletes() -- Get a list of relations to be deleted.
724
* The return value is the number of relations scheduled for termination.
725
* *ptr is set to point to a freshly-palloc'd array of RelFileNodes.
726
* If there are no relations to be deleted, *ptr is set to NULL.
728
* Note that the list does not include anything scheduled for termination
729
* by upper-level transactions.
732
smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr)
734
int nestLevel = GetCurrentTransactionNestLevel();
737
PendingRelDelete *pending;
740
for (pending = pendingDeletes; pending != NULL; pending = pending->next)
742
if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit)
750
rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode));
752
for (pending = pendingDeletes; pending != NULL; pending = pending->next)
754
if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit)
755
*rptr++ = pending->relnode;
761
* AtSubCommit_smgr() --- Take care of subtransaction commit.
763
* Reassign all items in the pending-deletes list to the parent transaction.
766
AtSubCommit_smgr(void)
768
int nestLevel = GetCurrentTransactionNestLevel();
769
PendingRelDelete *pending;
771
for (pending = pendingDeletes; pending != NULL; pending = pending->next)
773
if (pending->nestLevel >= nestLevel)
774
pending->nestLevel = nestLevel - 1;
779
* AtSubAbort_smgr() --- Take care of subtransaction abort.
781
* Delete created relations and forget about deleted relations.
782
* We can execute these operations immediately because we know this
783
* subtransaction will not commit.
786
AtSubAbort_smgr(void)
788
smgrDoPendingDeletes(false);
792
* smgrcommit() -- Prepare to commit changes made during the current
795
* This is called before we actually commit.
802
for (i = 0; i < NSmgr; i++)
804
if (smgrsw[i].smgr_commit)
806
if (!(*(smgrsw[i].smgr_commit)) ())
807
elog(ERROR, "transaction commit failed on %s: %m",
808
DatumGetCString(DirectFunctionCall1(smgrout,
815
* smgrabort() -- Clean up after transaction abort.
822
for (i = 0; i < NSmgr; i++)
824
if (smgrsw[i].smgr_abort)
826
if (!(*(smgrsw[i].smgr_abort)) ())
827
elog(ERROR, "transaction abort failed on %s: %m",
828
DatumGetCString(DirectFunctionCall1(smgrout,
835
* smgrsync() -- Sync files to disk at checkpoint time.
842
for (i = 0; i < NSmgr; i++)
844
if (smgrsw[i].smgr_sync)
846
if (!(*(smgrsw[i].smgr_sync)) ())
847
elog(ERROR, "storage sync failed on %s: %m",
848
DatumGetCString(DirectFunctionCall1(smgrout,
856
smgr_redo(XLogRecPtr lsn, XLogRecord *record)
858
uint8 info = record->xl_info & ~XLR_INFO_MASK;
860
if (info == XLOG_SMGR_CREATE)
862
xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
865
reln = smgropen(xlrec->rnode);
866
smgrcreate(reln, false, true);
868
else if (info == XLOG_SMGR_TRUNCATE)
870
xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record);
874
reln = smgropen(xlrec->rnode);
877
* First, force bufmgr to drop any buffers it has for the to-be-
878
* truncated blocks. We must do this, else subsequent
879
* XLogReadBuffer operations will not re-extend the file properly.
881
DropRelFileNodeBuffers(xlrec->rnode, false, xlrec->blkno);
883
/* Can't use smgrtruncate because it would try to xlog */
886
* Tell the free space map to forget anything it may have stored
887
* for the about-to-be-deleted blocks. We want to be sure it
888
* won't return bogus block numbers later on.
890
FreeSpaceMapTruncateRel(&reln->smgr_rnode, xlrec->blkno);
892
/* Do the truncation */
893
newblks = (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln,
896
if (newblks == InvalidBlockNumber)
898
(errcode_for_file_access(),
899
errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
900
reln->smgr_rnode.spcNode,
901
reln->smgr_rnode.dbNode,
902
reln->smgr_rnode.relNode,
906
elog(PANIC, "smgr_redo: unknown op code %u", info);
910
smgr_undo(XLogRecPtr lsn, XLogRecord *record)
912
/* Since we have no transactional WAL entries, should never undo */
913
elog(PANIC, "smgr_undo: cannot undo");
917
smgr_desc(char *buf, uint8 xl_info, char *rec)
919
uint8 info = xl_info & ~XLR_INFO_MASK;
921
if (info == XLOG_SMGR_CREATE)
923
xl_smgr_create *xlrec = (xl_smgr_create *) rec;
925
sprintf(buf + strlen(buf), "file create: %u/%u/%u",
926
xlrec->rnode.spcNode, xlrec->rnode.dbNode,
927
xlrec->rnode.relNode);
929
else if (info == XLOG_SMGR_TRUNCATE)
931
xl_smgr_truncate *xlrec = (xl_smgr_truncate *) rec;
933
sprintf(buf + strlen(buf), "file truncate: %u/%u/%u to %u blocks",
934
xlrec->rnode.spcNode, xlrec->rnode.dbNode,
935
xlrec->rnode.relNode, xlrec->blkno);
938
strcat(buf, "UNKNOWN");