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{
13231324List * rels = NIL ;
13241325List * relids = NIL ;
1325- List * seq_relids = NIL ;
1326- EState * estate ;
1327- ResultRelInfo * resultRelInfos ;
1328- ResultRelInfo * resultRelInfo ;
1329- SubTransactionId mySubid ;
1326+ List * relids_logged = NIL ;
13301327ListCell * cell ;
13311328
13321329/*
@@ -1350,6 +1347,9 @@ ExecuteTruncate(TruncateStmt *stmt)
13501347truncate_check_rel (rel );
13511348rels = lappend (rels ,rel );
13521349relids = 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
13541354if (recurse )
13551355{
@@ -1370,6 +1370,9 @@ ExecuteTruncate(TruncateStmt *stmt)
13701370truncate_check_rel (rel );
13711371rels = lappend (rels ,rel );
13721372relids = 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}
13751378else if (rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE )
@@ -1379,15 +1382,56 @@ ExecuteTruncate(TruncateStmt *stmt)
13791382errhint ("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{
13921436for (;;)
13931437{
@@ -1409,6 +1453,9 @@ ExecuteTruncate(TruncateStmt *stmt)
14091453truncate_check_rel (rel );
14101454rels = lappend (rels ,rel );
14111455relids = 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
14221469heap_truncate_check_FKs (rels , false);
14231470#else
1424- if (stmt -> behavior == DROP_RESTRICT )
1471+ if (behavior == DROP_RESTRICT )
14251472heap_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{
14361483foreach (cell ,rels )
14371484{
@@ -1586,6 +1633,41 @@ ExecuteTruncate(TruncateStmt *stmt)
15861633ResetSequence (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 */
16041686FreeExecutorState (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 );
16071693foreach (cell ,rels )
16081694{
16091695Relation rel = (Relation )lfirst (cell );