1616
1717#include "access/genam.h"
1818#include "access/heapam.h"
19+ #include "access/heapam_xlog.h"
1920#include "access/multixact.h"
2021#include "access/reloptions.h"
2122#include "access/relscan.h"
@@ -1322,11 +1323,7 @@ ExecuteTruncate(TruncateStmt *stmt)
13221323{
13231324 List * rels = NIL ;
13241325 List * relids = NIL ;
1325- List * seq_relids = NIL ;
1326- EState * estate ;
1327- ResultRelInfo * resultRelInfos ;
1328- ResultRelInfo * resultRelInfo ;
1329- SubTransactionId mySubid ;
1326+ List * relids_logged = NIL ;
13301327 ListCell * cell ;
13311328
13321329 /*
@@ -1350,6 +1347,9 @@ ExecuteTruncate(TruncateStmt *stmt)
13501347 truncate_check_rel (rel );
13511348 rels = lappend (rels , rel );
13521349 relids = lappend_oid (relids , myrelid );
1350+ /* Log this relation only if needed for logical decoding */
1351+ if (RelationIsLogicallyLogged (rel ))
1352+ relids_logged = lappend_oid (relids_logged , myrelid );
13531353
13541354 if (recurse )
13551355 {
@@ -1370,6 +1370,9 @@ ExecuteTruncate(TruncateStmt *stmt)
13701370 truncate_check_rel (rel );
13711371 rels = lappend (rels , rel );
13721372 relids = lappend_oid (relids , childrelid );
1373+ /* Log this relation only if needed for logical decoding */
1374+ if (RelationIsLogicallyLogged (rel ))
1375+ relids_logged = lappend_oid (relids_logged , childrelid );
13731376 }
13741377 }
13751378 else if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
@@ -1379,15 +1382,56 @@ ExecuteTruncate(TruncateStmt *stmt)
13791382 errhint ("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly." )));
13801383 }
13811384
1385+ ExecuteTruncateGuts (rels , relids , relids_logged ,
1386+ stmt -> behavior , stmt -> restart_seqs );
1387+
1388+ /* And close the rels */
1389+ foreach (cell , rels )
1390+ {
1391+ Relation rel = (Relation ) lfirst (cell );
1392+
1393+ heap_close (rel , NoLock );
1394+ }
1395+ }
1396+
1397+ /*
1398+ * ExecuteTruncateGuts
1399+ *
1400+ * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1401+ * command (see above) as well as replication subscribers that execute a
1402+ * replicated TRUNCATE action.
1403+ *
1404+ * explicit_rels is the list of Relations to truncate that the command
1405+ * specified. relids is the list of Oids corresponding to explicit_rels.
1406+ * relids_logged is the list of Oids (a subset of relids) that require
1407+ * WAL-logging. This is all a bit redundant, but the existing callers have
1408+ * this information handy in this form.
1409+ */
1410+ void
1411+ ExecuteTruncateGuts (List * explicit_rels , List * relids , List * relids_logged ,
1412+ DropBehavior behavior , bool restart_seqs )
1413+ {
1414+ List * rels ;
1415+ List * seq_relids = NIL ;
1416+ EState * estate ;
1417+ ResultRelInfo * resultRelInfos ;
1418+ ResultRelInfo * resultRelInfo ;
1419+ SubTransactionId mySubid ;
1420+ ListCell * cell ;
1421+ Oid * logrelids ;
1422+
13821423 /*
1424+ * Open, exclusive-lock, and check all the explicitly-specified relations
1425+ *
13831426 * In CASCADE mode, suck in all referencing relations as well. This
13841427 * requires multiple iterations to find indirectly-dependent relations. At
13851428 * each phase, we need to exclusive-lock new rels before looking for their
13861429 * dependencies, else we might miss something. Also, we check each rel as
13871430 * soon as we open it, to avoid a faux pas such as holding lock for a long
13881431 * time on a rel we have no permissions for.
13891432 */
1390- if (stmt -> behavior == DROP_CASCADE )
1433+ rels = list_copy (explicit_rels );
1434+ if (behavior == DROP_CASCADE )
13911435 {
13921436 for (;;)
13931437 {
@@ -1409,6 +1453,9 @@ ExecuteTruncate(TruncateStmt *stmt)
14091453 truncate_check_rel (rel );
14101454 rels = lappend (rels , rel );
14111455 relids = lappend_oid (relids , relid );
1456+ /* Log this relation only if needed for logical decoding */
1457+ if (RelationIsLogicallyLogged (rel ))
1458+ relids_logged = lappend_oid (relids_logged , relid );
14121459 }
14131460 }
14141461 }
@@ -1421,7 +1468,7 @@ ExecuteTruncate(TruncateStmt *stmt)
14211468#ifdef USE_ASSERT_CHECKING
14221469 heap_truncate_check_FKs (rels , false);
14231470#else
1424- if (stmt -> behavior == DROP_RESTRICT )
1471+ if (behavior == DROP_RESTRICT )
14251472 heap_truncate_check_FKs (rels , false);
14261473#endif
14271474
@@ -1431,7 +1478,7 @@ ExecuteTruncate(TruncateStmt *stmt)
14311478 * We want to do this early since it's pointless to do all the truncation
14321479 * work only to fail on sequence permissions.
14331480 */
1434- if (stmt -> restart_seqs )
1481+ if (restart_seqs )
14351482 {
14361483 foreach (cell , rels )
14371484 {
@@ -1586,6 +1633,41 @@ ExecuteTruncate(TruncateStmt *stmt)
15861633 ResetSequence (seq_relid );
15871634 }
15881635
1636+ /*
1637+ * Write a WAL record to allow this set of actions to be logically decoded.
1638+ *
1639+ * Assemble an array of relids so we can write a single WAL record for the
1640+ * whole action.
1641+ */
1642+ if (list_length (relids_logged ) > 0 )
1643+ {
1644+ xl_heap_truncate xlrec ;
1645+ int i = 0 ;
1646+
1647+ /* should only get here if wal_level >= logical */
1648+ Assert (XLogLogicalInfoActive ());
1649+
1650+ logrelids = palloc (list_length (relids_logged ) * sizeof (Oid ));
1651+ foreach (cell , relids_logged )
1652+ logrelids [i ++ ] = lfirst_oid (cell );
1653+
1654+ xlrec .dbId = MyDatabaseId ;
1655+ xlrec .nrelids = list_length (relids_logged );
1656+ xlrec .flags = 0 ;
1657+ if (behavior == DROP_CASCADE )
1658+ xlrec .flags |= XLH_TRUNCATE_CASCADE ;
1659+ if (restart_seqs )
1660+ xlrec .flags |= XLH_TRUNCATE_RESTART_SEQS ;
1661+
1662+ XLogBeginInsert ();
1663+ XLogRegisterData ((char * ) & xlrec , SizeOfHeapTruncate );
1664+ XLogRegisterData ((char * ) logrelids , list_length (relids_logged ) * sizeof (Oid ));
1665+
1666+ XLogSetRecordFlags (XLOG_INCLUDE_ORIGIN );
1667+
1668+ (void ) XLogInsert (RM_HEAP_ID , XLOG_HEAP_TRUNCATE );
1669+ }
1670+
15891671 /*
15901672 * Process all AFTER STATEMENT TRUNCATE triggers.
15911673 */
@@ -1603,7 +1685,11 @@ ExecuteTruncate(TruncateStmt *stmt)
16031685 /* We can clean up the EState now */
16041686 FreeExecutorState (estate );
16051687
1606- /* And close the rels (can't do this while EState still holds refs) */
1688+ /*
1689+ * Close any rels opened by CASCADE (can't do this while EState still
1690+ * holds refs)
1691+ */
1692+ rels = list_difference_ptr (rels , explicit_rels );
16071693 foreach (cell , rels )
16081694 {
16091695 Relation rel = (Relation ) lfirst (cell );
0 commit comments