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:
@ -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);
|
||||
|
Reference in New Issue
Block a user