1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-17 17:02:08 +03:00

Logical decoding of TRUNCATE

Add a new WAL record type for TRUNCATE, which is only used when
wal_level >= logical.  (For physical replication, TRUNCATE is already
replicated via SMGR records.)  Add new callback for logical decoding
output plugins to receive TRUNCATE actions.

Author: Simon Riggs <simon@2ndquadrant.com>
Author: Marco Nenciarini <marco.nenciarini@2ndquadrant.it>
Author: Peter Eisentraut <peter.eisentraut@2ndquadrant.com>
Reviewed-by: Petr Jelinek <petr.jelinek@2ndquadrant.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
This commit is contained in:
Peter Eisentraut
2018-04-07 11:17:56 -04:00
parent b508a56f2f
commit 5dfd1e5a66
15 changed files with 414 additions and 13 deletions

View File

@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/heapam_xlog.h"
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
@ -1322,11 +1323,7 @@ ExecuteTruncate(TruncateStmt *stmt)
{
List *rels = NIL;
List *relids = NIL;
List *seq_relids = NIL;
EState *estate;
ResultRelInfo *resultRelInfos;
ResultRelInfo *resultRelInfo;
SubTransactionId mySubid;
List *relids_logged = NIL;
ListCell *cell;
/*
@ -1350,6 +1347,9 @@ ExecuteTruncate(TruncateStmt *stmt)
truncate_check_rel(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, myrelid);
/* Log this relation only if needed for logical decoding */
if (RelationIsLogicallyLogged(rel))
relids_logged = lappend_oid(relids_logged, myrelid);
if (recurse)
{
@ -1370,6 +1370,9 @@ ExecuteTruncate(TruncateStmt *stmt)
truncate_check_rel(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, childrelid);
/* Log this relation only if needed for logical decoding */
if (RelationIsLogicallyLogged(rel))
relids_logged = lappend_oid(relids_logged, childrelid);
}
}
else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
@ -1379,7 +1382,47 @@ ExecuteTruncate(TruncateStmt *stmt)
errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
}
ExecuteTruncateGuts(rels, relids, relids_logged,
stmt->behavior, stmt->restart_seqs);
/* And close the rels */
foreach(cell, rels)
{
Relation rel = (Relation) lfirst(cell);
heap_close(rel, NoLock);
}
}
/*
* ExecuteTruncateGuts
*
* Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
* command (see above) as well as replication subscribers that execute a
* replicated TRUNCATE action.
*
* explicit_rels is the list of Relations to truncate that the command
* specified. relids is the list of Oids corresponding to explicit_rels.
* relids_logged is the list of Oids (a subset of relids) that require
* WAL-logging. This is all a bit redundant, but the existing callers have
* this information handy in this form.
*/
void
ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
DropBehavior behavior, bool restart_seqs)
{
List *rels;
List *seq_relids = NIL;
EState *estate;
ResultRelInfo *resultRelInfos;
ResultRelInfo *resultRelInfo;
SubTransactionId mySubid;
ListCell *cell;
Oid *logrelids;
/*
* Open, exclusive-lock, and check all the explicitly-specified relations
*
* In CASCADE mode, suck in all referencing relations as well. This
* requires multiple iterations to find indirectly-dependent relations. At
* each phase, we need to exclusive-lock new rels before looking for their
@ -1387,7 +1430,8 @@ ExecuteTruncate(TruncateStmt *stmt)
* soon as we open it, to avoid a faux pas such as holding lock for a long
* time on a rel we have no permissions for.
*/
if (stmt->behavior == DROP_CASCADE)
rels = list_copy(explicit_rels);
if (behavior == DROP_CASCADE)
{
for (;;)
{
@ -1409,6 +1453,9 @@ ExecuteTruncate(TruncateStmt *stmt)
truncate_check_rel(rel);
rels = lappend(rels, rel);
relids = lappend_oid(relids, relid);
/* Log this relation only if needed for logical decoding */
if (RelationIsLogicallyLogged(rel))
relids_logged = lappend_oid(relids_logged, relid);
}
}
}
@ -1421,7 +1468,7 @@ ExecuteTruncate(TruncateStmt *stmt)
#ifdef USE_ASSERT_CHECKING
heap_truncate_check_FKs(rels, false);
#else
if (stmt->behavior == DROP_RESTRICT)
if (behavior == DROP_RESTRICT)
heap_truncate_check_FKs(rels, false);
#endif
@ -1431,7 +1478,7 @@ ExecuteTruncate(TruncateStmt *stmt)
* We want to do this early since it's pointless to do all the truncation
* work only to fail on sequence permissions.
*/
if (stmt->restart_seqs)
if (restart_seqs)
{
foreach(cell, rels)
{
@ -1586,6 +1633,41 @@ ExecuteTruncate(TruncateStmt *stmt)
ResetSequence(seq_relid);
}
/*
* Write a WAL record to allow this set of actions to be logically decoded.
*
* Assemble an array of relids so we can write a single WAL record for the
* whole action.
*/
if (list_length(relids_logged) > 0)
{
xl_heap_truncate xlrec;
int i = 0;
/* should only get here if wal_level >= logical */
Assert(XLogLogicalInfoActive());
logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
foreach (cell, relids_logged)
logrelids[i++] = lfirst_oid(cell);
xlrec.dbId = MyDatabaseId;
xlrec.nrelids = list_length(relids_logged);
xlrec.flags = 0;
if (behavior == DROP_CASCADE)
xlrec.flags |= XLH_TRUNCATE_CASCADE;
if (restart_seqs)
xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
XLogBeginInsert();
XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
(void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
}
/*
* Process all AFTER STATEMENT TRUNCATE triggers.
*/
@ -1603,7 +1685,11 @@ ExecuteTruncate(TruncateStmt *stmt)
/* We can clean up the EState now */
FreeExecutorState(estate);
/* And close the rels (can't do this while EState still holds refs) */
/*
* Close any rels opened by CASCADE (can't do this while EState still
* holds refs)
*/
rels = list_difference_ptr(rels, explicit_rels);
foreach(cell, rels)
{
Relation rel = (Relation) lfirst(cell);