On 2021/04/08 18:25, Fujii Masao wrote:
Anyway, attached is the updated version of the patch. This is still based on 
the latest Kazutaka-san's patch. That is, extra list for ONLY is still passed 
to FDW. What about committing this version at first? Then we can continue the 
discussion and change the behavior later if necessary.

The patch failed to be applied because of recent commit.
Attached is the updated version of the patch.

Regards,

--
Fujii Masao
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION
diff --git a/contrib/postgres_fdw/connection.c 
b/contrib/postgres_fdw/connection.c
index 6a61d83862..82aa14a65d 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -92,7 +92,6 @@ static PGconn *connect_pg_server(ForeignServer *server, 
UserMapping *user);
 static void disconnect_pg_server(ConnCacheEntry *entry);
 static void check_conn_params(const char **keywords, const char **values, 
UserMapping *user);
 static void configure_remote_session(PGconn *conn);
-static void do_sql_command(PGconn *conn, const char *sql);
 static void begin_remote_xact(ConnCacheEntry *entry);
 static void pgfdw_xact_callback(XactEvent event, void *arg);
 static void pgfdw_subxact_callback(SubXactEvent event,
@@ -568,7 +567,7 @@ configure_remote_session(PGconn *conn)
 /*
  * Convenience subroutine to issue a non-data-returning SQL command to remote
  */
-static void
+void
 do_sql_command(PGconn *conn, const char *sql)
 {
        PGresult   *res;
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 5aa3455e30..bdc4c3620d 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -56,6 +56,7 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
+#include "commands/tablecmds.h"
 
 /*
  * Global context for foreign_expr_walker's search of an expression tree.
@@ -2172,6 +2173,43 @@ deparseAnalyzeSql(StringInfo buf, Relation rel, List 
**retrieved_attrs)
        deparseRelation(buf, rel);
 }
 
+/*
+ * Construct a simple "TRUNCATE rel" statement
+ */
+void
+deparseTruncateSql(StringInfo buf,
+                                  List *rels,
+                                  List *rels_extra,
+                                  DropBehavior behavior,
+                                  bool restart_seqs)
+{
+       ListCell   *lc1,
+                          *lc2;
+
+       appendStringInfoString(buf, "TRUNCATE ");
+
+       forboth(lc1, rels, lc2, rels_extra)
+       {
+               Relation        rel = lfirst(lc1);
+               int                     extra = lfirst_int(lc2);
+
+               if (lc1 != list_head(rels))
+                       appendStringInfoString(buf, ", ");
+               if (extra & TRUNCATE_REL_CONTEXT_ONLY)
+                       appendStringInfoString(buf, "ONLY ");
+
+               deparseRelation(buf, rel);
+       }
+
+       appendStringInfo(buf, " %s IDENTITY",
+                                        restart_seqs ? "RESTART" : "CONTINUE");
+
+       if (behavior == DROP_RESTRICT)
+               appendStringInfoString(buf, " RESTRICT");
+       else if (behavior == DROP_CASCADE)
+               appendStringInfoString(buf, " CASCADE");
+}
+
 /*
  * Construct name to use for given column, and emit it into buf.
  * If it has a column_name FDW option, use that instead of attribute name.
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out 
b/contrib/postgres_fdw/expected/postgres_fdw.out
index eeb6ae79d0..7f69fa0054 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -8215,6 +8215,205 @@ select * from rem3;
 drop foreign table rem3;
 drop table loc3;
 -- ===================================================================
+-- test for TRUNCATE
+-- ===================================================================
+CREATE TABLE tru_rtable0 (id int primary key);
+CREATE TABLE tru_rtable1 (id int primary key);
+CREATE FOREIGN TABLE tru_ftable (id int)
+       SERVER loopback OPTIONS (table_name 'tru_rtable0');
+INSERT INTO tru_rtable0 (SELECT x FROM generate_series(1,10) x);
+CREATE TABLE tru_ptable (id int) PARTITION BY HASH(id);
+CREATE TABLE tru_ptable__p0 PARTITION OF tru_ptable
+                            FOR VALUES WITH (MODULUS 2, REMAINDER 0);
+CREATE FOREIGN TABLE tru_ftable__p1 PARTITION OF tru_ptable
+                                    FOR VALUES WITH (MODULUS 2, REMAINDER 1)
+       SERVER loopback OPTIONS (table_name 'tru_rtable1');
+INSERT INTO tru_ptable (SELECT x FROM generate_series(11,20) x);
+CREATE TABLE tru_pk_table(id int primary key);
+CREATE TABLE tru_fk_table(fkey int references tru_pk_table(id));
+INSERT INTO tru_pk_table (SELECT x FROM generate_series(1,10) x);
+INSERT INTO tru_fk_table (SELECT x % 10 + 1 FROM generate_series(5,25) x);
+CREATE FOREIGN TABLE tru_pk_ftable (id int)
+       SERVER loopback OPTIONS (table_name 'tru_pk_table');
+CREATE TABLE tru_rtable_parent (id int);
+CREATE TABLE tru_rtable_child (id int);
+CREATE FOREIGN TABLE tru_ftable_parent (id int)
+       SERVER loopback OPTIONS (table_name 'tru_rtable_parent');
+CREATE FOREIGN TABLE tru_ftable_child () INHERITS (tru_ftable_parent)
+       SERVER loopback OPTIONS (table_name 'tru_rtable_child');
+INSERT INTO tru_rtable_parent (SELECT x FROM generate_series(1,8) x);
+INSERT INTO tru_rtable_child  (SELECT x FROM generate_series(10, 18) x);
+-- normal truncate
+SELECT sum(id) FROM tru_ftable;        -- 55
+ sum 
+-----
+  55
+(1 row)
+
+TRUNCATE tru_ftable;
+SELECT count(*) FROM tru_rtable0;              -- 0
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM tru_ftable;               -- 0
+ count 
+-------
+     0
+(1 row)
+
+-- 'truncatable' option
+ALTER SERVER loopback OPTIONS (ADD truncatable 'false');
+TRUNCATE tru_ftable;                   -- error
+ERROR:  foreign table "tru_ftable" does not allow truncates
+ALTER FOREIGN TABLE tru_ftable OPTIONS (ADD truncatable 'true');
+TRUNCATE tru_ftable;                   -- accepted
+ALTER FOREIGN TABLE tru_ftable OPTIONS (SET truncatable 'false');
+TRUNCATE tru_ftable;                   -- error
+ERROR:  foreign table "tru_ftable" does not allow truncates
+ALTER SERVER loopback OPTIONS (DROP truncatable);
+ALTER FOREIGN TABLE tru_ftable OPTIONS (SET truncatable 'false');
+TRUNCATE tru_ftable;                   -- error
+ERROR:  foreign table "tru_ftable" does not allow truncates
+ALTER FOREIGN TABLE tru_ftable OPTIONS (SET truncatable 'true');
+TRUNCATE tru_ftable;                   -- accepted
+-- partitioned table with both local and foreign tables as partitions
+SELECT sum(id) FROM tru_ptable;        -- 155
+ sum 
+-----
+ 155
+(1 row)
+
+TRUNCATE tru_ptable;
+SELECT count(*) FROM tru_ptable;               -- 0
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM tru_ptable__p0;   -- 0
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM tru_ftable__p1;   -- 0
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM tru_rtable1;              -- 0
+ count 
+-------
+     0
+(1 row)
+
+-- 'CASCADE' option
+SELECT sum(id) FROM tru_pk_ftable;      -- 55
+ sum 
+-----
+  55
+(1 row)
+
+TRUNCATE tru_pk_ftable;        -- failed by FK reference
+ERROR:  cannot truncate a table referenced in a foreign key constraint
+DETAIL:  Table "tru_fk_table" references "tru_pk_table".
+HINT:  Truncate table "tru_fk_table" at the same time, or use TRUNCATE ... 
CASCADE.
+CONTEXT:  remote SQL command: TRUNCATE public.tru_pk_table CONTINUE IDENTITY 
RESTRICT
+TRUNCATE tru_pk_ftable CASCADE;
+SELECT count(*) FROM tru_pk_ftable;    -- 0
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM tru_fk_table;             -- also truncated,0
+ count 
+-------
+     0
+(1 row)
+
+-- truncate two tables at a command
+INSERT INTO tru_ftable (SELECT x FROM generate_series(1,8) x);
+INSERT INTO tru_pk_ftable (SELECT x FROM generate_series(3,10) x);
+SELECT count(*) from tru_ftable; -- 8
+ count 
+-------
+     8
+(1 row)
+
+SELECT count(*) from tru_pk_ftable; -- 8
+ count 
+-------
+     8
+(1 row)
+
+TRUNCATE tru_ftable, tru_pk_ftable CASCADE;
+SELECT count(*) from tru_ftable; -- 0
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) from tru_pk_ftable; -- 0
+ count 
+-------
+     0
+(1 row)
+
+-- truncate with ONLY clause
+TRUNCATE ONLY tru_ftable_parent;
+SELECT sum(id) FROM tru_ftable_parent;  -- 126
+ sum 
+-----
+ 126
+(1 row)
+
+TRUNCATE tru_ftable_parent;
+SELECT count(*) FROM tru_ftable_parent; -- 0
+ count 
+-------
+     0
+(1 row)
+
+-- in case when remote table has inherited children
+CREATE TABLE tru_rtable0_child () INHERITS (tru_rtable0);
+INSERT INTO tru_rtable0 (SELECT x FROM generate_series(5,9) x);
+INSERT INTO tru_rtable0_child (SELECT x FROM generate_series(10,14) x);
+SELECT sum(id) FROM tru_ftable;   -- 95
+ sum 
+-----
+  95
+(1 row)
+
+TRUNCATE ONLY tru_ftable;              -- truncate only parent portion
+SELECT sum(id) FROM tru_ftable;   -- 60
+ sum 
+-----
+  60
+(1 row)
+
+INSERT INTO tru_rtable0 (SELECT x FROM generate_series(21,25) x);
+SELECT sum(id) FROM tru_ftable;                -- 175
+ sum 
+-----
+ 175
+(1 row)
+
+TRUNCATE tru_ftable;                   -- truncate both of parent and child
+SELECT count(*) FROM tru_ftable;    -- empty
+ count 
+-------
+     0
+(1 row)
+
+-- cleanup
+DROP FOREIGN TABLE tru_ftable_parent, tru_ftable_child, 
tru_pk_ftable,tru_ftable__p1,tru_ftable;
+DROP TABLE tru_rtable0, tru_rtable1, tru_ptable, tru_ptable__p0, tru_pk_table, 
tru_fk_table,
+tru_rtable_parent,tru_rtable_child, tru_rtable0_child;
+-- ===================================================================
 -- test IMPORT FOREIGN SCHEMA
 -- ===================================================================
 CREATE SCHEMA import_source;
@@ -8917,7 +9116,7 @@ DO $d$
     END;
 $d$;
 ERROR:  invalid option "password"
-HINT:  Valid options in this context are: service, passfile, channel_binding, 
connect_timeout, dbname, host, hostaddr, port, options, application_name, 
keepalives, keepalives_idle, keepalives_interval, keepalives_count, 
tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, 
sslcrl, sslcrldir, sslsni, requirepeer, ssl_min_protocol_version, 
ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, 
use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, 
fetch_size, batch_size, async_capable, keep_connections
+HINT:  Valid options in this context are: service, passfile, channel_binding, 
connect_timeout, dbname, host, hostaddr, port, options, application_name, 
keepalives, keepalives_idle, keepalives_interval, keepalives_count, 
tcp_user_timeout, sslmode, sslcompression, sslcert, sslkey, sslrootcert, 
sslcrl, sslcrldir, sslsni, requirepeer, ssl_min_protocol_version, 
ssl_max_protocol_version, gssencmode, krbsrvname, gsslib, target_session_attrs, 
use_remote_estimate, fdw_startup_cost, fdw_tuple_cost, extensions, updatable, 
truncatable, fetch_size, batch_size, async_capable, keep_connections
 CONTEXT:  SQL statement "ALTER SERVER loopback_nopw OPTIONS (ADD password 
'dummypw')"
 PL/pgSQL function inline_code_block line 3 at EXECUTE
 -- If we add a password for our user mapping instead, we should get a different
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index f1d0c8bd41..672b55a808 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -108,6 +108,7 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
                 */
                if (strcmp(def->defname, "use_remote_estimate") == 0 ||
                        strcmp(def->defname, "updatable") == 0 ||
+                       strcmp(def->defname, "truncatable") == 0 ||
                        strcmp(def->defname, "async_capable") == 0 ||
                        strcmp(def->defname, "keep_connections") == 0)
                {
@@ -213,6 +214,9 @@ InitPgFdwOptions(void)
                /* updatable is available on both server and table */
                {"updatable", ForeignServerRelationId, false},
                {"updatable", ForeignTableRelationId, false},
+               /* truncatable is available on both server and table */
+               {"truncatable", ForeignServerRelationId, false},
+               {"truncatable", ForeignTableRelationId, false},
                /* fetch_size is available on both server and table */
                {"fetch_size", ForeignServerRelationId, false},
                {"fetch_size", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c 
b/contrib/postgres_fdw/postgres_fdw.c
index b6442070a3..c590f374c6 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -400,6 +400,10 @@ static void postgresExplainForeignModify(ModifyTableState 
*mtstate,
                                                                                
 ExplainState *es);
 static void postgresExplainDirectModify(ForeignScanState *node,
                                                                                
ExplainState *es);
+static void postgresExecForeignTruncate(List *rels,
+                                                                               
List *rels_extra,
+                                                                               
DropBehavior behavior,
+                                                                               
bool restart_seqs);
 static bool postgresAnalyzeForeignTable(Relation relation,
                                                                                
AcquireSampleRowsFunc *func,
                                                                                
BlockNumber *totalpages);
@@ -588,6 +592,9 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
        routine->ExplainForeignModify = postgresExplainForeignModify;
        routine->ExplainDirectModify = postgresExplainDirectModify;
 
+       /* Support function for TRUNCATE */
+       routine->ExecForeignTruncate = postgresExecForeignTruncate;
+
        /* Support functions for ANALYZE */
        routine->AnalyzeForeignTable = postgresAnalyzeForeignTable;
 
@@ -2868,6 +2875,102 @@ postgresExplainDirectModify(ForeignScanState *node, 
ExplainState *es)
        }
 }
 
+/*
+ * postgresExecForeignTruncate
+ *             Truncate one or more foreign tables
+ */
+static void
+postgresExecForeignTruncate(List *rels,
+                                                       List *rels_extra,
+                                                       DropBehavior behavior,
+                                                       bool restart_seqs)
+{
+       Oid                     serverid = InvalidOid;
+       UserMapping *user = NULL;
+       PGconn     *conn = NULL;
+       StringInfoData sql;
+       ListCell   *lc;
+       bool            server_truncatable = true;
+
+       /*
+        * By default, all postgres_fdw foreign tables are assumed truncatable.
+        * This can be overridden by a per-server setting, which in turn can be
+        * overridden by a per-table setting.
+        */
+       foreach(lc, rels)
+       {
+               ForeignServer *server = NULL;
+               Relation        rel = lfirst(lc);
+               ForeignTable *table = GetForeignTable(RelationGetRelid(rel));
+               ListCell   *cell;
+               bool            truncatable;
+
+               /*
+                * First time through, determine whether the foreign server 
allows
+                * truncates. Since all specified foreign tables are assumed to 
belong
+                * to the same foreign server, this result can be used for other
+                * foreign tables.
+                */
+               if (!OidIsValid(serverid))
+               {
+                       serverid = table->serverid;
+                       server = GetForeignServer(serverid);
+
+                       foreach(cell, server->options)
+                       {
+                               DefElem    *defel = (DefElem *) lfirst(cell);
+
+                               if (strcmp(defel->defname, "truncatable") == 0)
+                               {
+                                       server_truncatable = 
defGetBoolean(defel);
+                                       break;
+                               }
+                       }
+               }
+
+               /*
+                * Confirm that all specified foreign tables belong to the same
+                * foreign server.
+                */
+               Assert(table->serverid == serverid);
+
+               /* Determine whether this foreign table allows truncations */
+               truncatable = server_truncatable;
+               foreach(cell, table->options)
+               {
+                       DefElem    *defel = (DefElem *) lfirst(cell);
+
+                       if (strcmp(defel->defname, "truncatable") == 0)
+                       {
+                               truncatable = defGetBoolean(defel);
+                               break;
+                       }
+               }
+
+               if (!truncatable)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                        errmsg("foreign table \"%s\" does not 
allow truncates",
+                                                       
RelationGetRelationName(rel))));
+       }
+       Assert(OidIsValid(serverid));
+
+       /*
+        * Get connection to the foreign server.  Connection manager will
+        * establish new connection if necessary.
+        */
+       user = GetUserMapping(GetUserId(), serverid);
+       conn = GetConnection(user, false, NULL);
+
+       /* Construct the TRUNCATE command string */
+       initStringInfo(&sql);
+       deparseTruncateSql(&sql, rels, rels_extra, behavior, restart_seqs);
+
+       /* Issue the TRUNCATE command to remote server */
+       do_sql_command(conn, sql.data);
+
+       pfree(sql.data);
+}
 
 /*
  * estimate_path_cost_size
diff --git a/contrib/postgres_fdw/postgres_fdw.h 
b/contrib/postgres_fdw/postgres_fdw.h
index 88d94da6f6..5d44b75314 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -145,6 +145,7 @@ extern PGconn *GetConnection(UserMapping *user, bool 
will_prep_stmt,
 extern void ReleaseConnection(PGconn *conn);
 extern unsigned int GetCursorNumber(PGconn *conn);
 extern unsigned int GetPrepStmtNumber(PGconn *conn);
+extern void do_sql_command(PGconn *conn, const char *sql);
 extern PGresult *pgfdw_get_result(PGconn *conn, const char *query);
 extern PGresult *pgfdw_exec_query(PGconn *conn, const char *query,
                                                                  
PgFdwConnState *state);
@@ -206,6 +207,11 @@ extern void deparseDirectDeleteSql(StringInfo buf, 
PlannerInfo *root,
 extern void deparseAnalyzeSizeSql(StringInfo buf, Relation rel);
 extern void deparseAnalyzeSql(StringInfo buf, Relation rel,
                                                          List 
**retrieved_attrs);
+extern void deparseTruncateSql(StringInfo buf,
+                                                          List *rels,
+                                                          List *rels_extra,
+                                                          DropBehavior 
behavior,
+                                                          bool restart_seqs);
 extern void deparseStringLiteral(StringInfo buf, const char *val);
 extern Expr *find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel);
 extern Expr *find_em_expr_for_input_target(PlannerInfo *root,
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql 
b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 3b4f90a99c..7487096eac 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2351,6 +2351,107 @@ select * from rem3;
 drop foreign table rem3;
 drop table loc3;
 
+-- ===================================================================
+-- test for TRUNCATE
+-- ===================================================================
+CREATE TABLE tru_rtable0 (id int primary key);
+CREATE TABLE tru_rtable1 (id int primary key);
+CREATE FOREIGN TABLE tru_ftable (id int)
+       SERVER loopback OPTIONS (table_name 'tru_rtable0');
+INSERT INTO tru_rtable0 (SELECT x FROM generate_series(1,10) x);
+
+CREATE TABLE tru_ptable (id int) PARTITION BY HASH(id);
+CREATE TABLE tru_ptable__p0 PARTITION OF tru_ptable
+                            FOR VALUES WITH (MODULUS 2, REMAINDER 0);
+CREATE FOREIGN TABLE tru_ftable__p1 PARTITION OF tru_ptable
+                                    FOR VALUES WITH (MODULUS 2, REMAINDER 1)
+       SERVER loopback OPTIONS (table_name 'tru_rtable1');
+INSERT INTO tru_ptable (SELECT x FROM generate_series(11,20) x);
+
+CREATE TABLE tru_pk_table(id int primary key);
+CREATE TABLE tru_fk_table(fkey int references tru_pk_table(id));
+INSERT INTO tru_pk_table (SELECT x FROM generate_series(1,10) x);
+INSERT INTO tru_fk_table (SELECT x % 10 + 1 FROM generate_series(5,25) x);
+CREATE FOREIGN TABLE tru_pk_ftable (id int)
+       SERVER loopback OPTIONS (table_name 'tru_pk_table');
+
+CREATE TABLE tru_rtable_parent (id int);
+CREATE TABLE tru_rtable_child (id int);
+CREATE FOREIGN TABLE tru_ftable_parent (id int)
+       SERVER loopback OPTIONS (table_name 'tru_rtable_parent');
+CREATE FOREIGN TABLE tru_ftable_child () INHERITS (tru_ftable_parent)
+       SERVER loopback OPTIONS (table_name 'tru_rtable_child');
+INSERT INTO tru_rtable_parent (SELECT x FROM generate_series(1,8) x);
+INSERT INTO tru_rtable_child  (SELECT x FROM generate_series(10, 18) x);
+
+-- normal truncate
+SELECT sum(id) FROM tru_ftable;        -- 55
+TRUNCATE tru_ftable;
+SELECT count(*) FROM tru_rtable0;              -- 0
+SELECT count(*) FROM tru_ftable;               -- 0
+
+-- 'truncatable' option
+ALTER SERVER loopback OPTIONS (ADD truncatable 'false');
+TRUNCATE tru_ftable;                   -- error
+ALTER FOREIGN TABLE tru_ftable OPTIONS (ADD truncatable 'true');
+TRUNCATE tru_ftable;                   -- accepted
+ALTER FOREIGN TABLE tru_ftable OPTIONS (SET truncatable 'false');
+TRUNCATE tru_ftable;                   -- error
+ALTER SERVER loopback OPTIONS (DROP truncatable);
+ALTER FOREIGN TABLE tru_ftable OPTIONS (SET truncatable 'false');
+TRUNCATE tru_ftable;                   -- error
+ALTER FOREIGN TABLE tru_ftable OPTIONS (SET truncatable 'true');
+TRUNCATE tru_ftable;                   -- accepted
+
+-- partitioned table with both local and foreign tables as partitions
+SELECT sum(id) FROM tru_ptable;        -- 155
+TRUNCATE tru_ptable;
+SELECT count(*) FROM tru_ptable;               -- 0
+SELECT count(*) FROM tru_ptable__p0;   -- 0
+SELECT count(*) FROM tru_ftable__p1;   -- 0
+SELECT count(*) FROM tru_rtable1;              -- 0
+
+-- 'CASCADE' option
+SELECT sum(id) FROM tru_pk_ftable;      -- 55
+TRUNCATE tru_pk_ftable;        -- failed by FK reference
+TRUNCATE tru_pk_ftable CASCADE;
+SELECT count(*) FROM tru_pk_ftable;    -- 0
+SELECT count(*) FROM tru_fk_table;             -- also truncated,0
+
+-- truncate two tables at a command
+INSERT INTO tru_ftable (SELECT x FROM generate_series(1,8) x);
+INSERT INTO tru_pk_ftable (SELECT x FROM generate_series(3,10) x);
+SELECT count(*) from tru_ftable; -- 8
+SELECT count(*) from tru_pk_ftable; -- 8
+TRUNCATE tru_ftable, tru_pk_ftable CASCADE;
+SELECT count(*) from tru_ftable; -- 0
+SELECT count(*) from tru_pk_ftable; -- 0
+
+-- truncate with ONLY clause
+TRUNCATE ONLY tru_ftable_parent;
+SELECT sum(id) FROM tru_ftable_parent;  -- 126
+TRUNCATE tru_ftable_parent;
+SELECT count(*) FROM tru_ftable_parent; -- 0
+
+-- in case when remote table has inherited children
+CREATE TABLE tru_rtable0_child () INHERITS (tru_rtable0);
+INSERT INTO tru_rtable0 (SELECT x FROM generate_series(5,9) x);
+INSERT INTO tru_rtable0_child (SELECT x FROM generate_series(10,14) x);
+SELECT sum(id) FROM tru_ftable;   -- 95
+
+TRUNCATE ONLY tru_ftable;              -- truncate only parent portion
+SELECT sum(id) FROM tru_ftable;   -- 60
+
+INSERT INTO tru_rtable0 (SELECT x FROM generate_series(21,25) x);
+SELECT sum(id) FROM tru_ftable;                -- 175
+TRUNCATE tru_ftable;                   -- truncate both of parent and child
+SELECT count(*) FROM tru_ftable;    -- empty
+
+-- cleanup
+DROP FOREIGN TABLE tru_ftable_parent, tru_ftable_child, 
tru_pk_ftable,tru_ftable__p1,tru_ftable;
+DROP TABLE tru_rtable0, tru_rtable1, tru_ptable, tru_ptable__p0, tru_pk_table, 
tru_fk_table,
+tru_rtable_parent,tru_rtable_child, tru_rtable0_child;
+
 -- ===================================================================
 -- test IMPORT FOREIGN SCHEMA
 -- ===================================================================
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 0f2397df49..46ab2e6708 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1065,6 +1065,50 @@ EndDirectModify(ForeignScanState *node);
 
    </sect2>
 
+   <sect2 id="fdw-callbacks-truncate">
+    <title>FDW Routines for Truncate</title>
+<programlisting>
+void
+ExecForeignTruncate(List *rels, List *rels_extra,
+                    DropBehavior behavior, bool restart_seqs);
+</programlisting>
+    <para>
+     Truncate a set of foreign tables defined by
+     <literal>rels</literal> belonging to the same foreign server.
+     This optional function is called during execution of
+     <command>TRUNCATE</command> for each foreign server involved
+     in one <command>TRUNCATE</command> command (note that invocations
+     are not per foreign table).
+
+     <literal>rels_extra</literal> delivers extra information about
+     the context where the foreign tables are truncated. It is a list of 
integers and has same length with
+     <literal>rels</literal>. <literal>TRUNCATE_REL_CONTEXT_NORMAL</literal> 
means that
+     the foreign table is specified WITHOUT <literal>ONLY</literal> clause, 
and <literal>TRUNCATE_REL_CONTEXT_ONLY</literal> means
+     specified WITH <literal>ONLY</literal> clause. 
<literal>TRUNCATE_REL_CONTEXT_CASCADING</literal> 
+     value means that foreign tables are not specified in the 
<command>TRUNCATE</command>, 
+     but truncated due to dependency (for example, partition table).
+    </para>
+
+    <para>
+     If the <function>ExecForeignTruncate</function> pointer is set to
+     <literal>NULL</literal>, attempts to truncate the foreign table will
+     fail with an error message.
+    </para>
+    
+    <para>
+     <literal>behavior</literal> defines how foreign tables should
+     be truncated, using as possible values <literal>DROP_RESTRICT</literal>
+     and <literal>DROP_CASCADE</literal> (to map with the equivalents of
+     <command>TRUNCATE</command>).
+    </para>
+    
+    <para>
+     <literal>restart_seqs</literal> is set to <literal>true</literal>
+     if <literal>RESTART IDENTITY</literal> was supplied in the
+     <command>TRUNCATE</command>.
+    </para>
+   </sect2>
+
    <sect2 id="fdw-callbacks-row-locking">
     <title>FDW Routines for Row Locking</title>
 
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index b0792a13b1..fd34956936 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -63,9 +63,10 @@
  <para>
   Now you need only <command>SELECT</command> from a foreign table to access
   the data stored in its underlying remote table.  You can also modify
-  the remote table using <command>INSERT</command>, <command>UPDATE</command>, 
or
-  <command>DELETE</command>.  (Of course, the remote user you have specified
-  in your user mapping must have privileges to do these things.)
+  the remote table using <command>INSERT</command>, <command>UPDATE</command>,
+  <command>DELETE</command>, or <command>TRUNCATE</command>.
+  (Of course, the remote user you have specified in your user mapping must
+  have privileges to do these things.)
  </para>
 
  <para>
@@ -436,6 +437,31 @@ OPTIONS (ADD password_required 'false');
    </variablelist>
   </sect3>
 
+  <sect3>
+   <title>Truncatability Options</title>
+
+   <para>
+    By default all foreign tables using <filename>postgres_fdw</filename> are 
assumed
+    to be truncatable.  This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>truncatable</literal></term>
+     <listitem>
+      <para>
+       This option controls whether <filename>postgres_fdw</filename> allows
+       foreign tables to be truncated using <command>TRUNCATE</command>
+       command. It can be specified for a foreign table or a foreign server.
+       A table-level option overrides a server-level option.
+       The default is <literal>true</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </sect3>
+
   <sect3>
    <title>Importing Options</title>
 
diff --git a/doc/src/sgml/ref/truncate.sgml b/doc/src/sgml/ref/truncate.sgml
index 91cdac5562..7899106ba1 100644
--- a/doc/src/sgml/ref/truncate.sgml
+++ b/doc/src/sgml/ref/truncate.sgml
@@ -172,9 +172,8 @@ TRUNCATE [ TABLE ] [ ONLY ] <replaceable 
class="parameter">name</replaceable> [
   </para>
 
   <para>
-   <command>TRUNCATE</command> is not currently supported for foreign tables.
-   This implies that if a specified table has any descendant tables that are
-   foreign, the command will fail.
+    <command>TRUNCATE</command> can be used for foreign tables if
+     the foreign data wrapper supports, for instance, see <xref 
linkend="postgres-fdw"/>.
   </para>
  </refsect1>
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 87f9bdaef0..7bad33bafb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -59,6 +59,7 @@
 #include "commands/typecmds.h"
 #include "commands/user.h"
 #include "executor/executor.h"
+#include "foreign/fdwapi.h"
 #include "foreign/foreign.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -310,6 +311,21 @@ struct DropRelationCallbackState
 #define                ATT_FOREIGN_TABLE               0x0020
 #define                ATT_PARTITIONED_INDEX   0x0040
 
+/*
+ * ForeignTruncateInfo
+ *
+ * Information related to truncation of foreign tables.  This is used for
+ * the elements in a hash table. It uses the server OID as lookup key,
+ * and includes a per-server list of all foreign tables involved in the
+ * truncation.
+ */
+typedef struct ForeignTruncateInfo
+{
+       Oid                     serverid;
+       List       *rels;
+       List       *rels_extra;
+} ForeignTruncateInfo;
+
 /*
  * Partition tables are expected to be dropped when the parent partitioned
  * table gets dropped. Hence for partitioning we use AUTO dependency.
@@ -1589,7 +1605,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid 
relOid, Oid oldRelOid,
  *
  * This is a multi-relation truncate.  We first open and grab exclusive
  * lock on all relations involved, checking permissions and otherwise
- * verifying that the relation is OK for truncation.  In CASCADE mode,
+ * verifying that the relation is OK for truncation.  Note that if relations
+ * are foreign tables, at this stage, we have not yet checked that their
+ * foreign data in external data sources are OK for truncation.  These are
+ * checked when foreign data are actually truncated later.  In CASCADE mode,
  * relations having FK references to the targeted relations are automatically
  * added to the group; in RESTRICT mode, we check that all FK references are
  * internal to the group that's being truncated.  Finally all the relations
@@ -1600,6 +1619,7 @@ ExecuteTruncate(TruncateStmt *stmt)
 {
        List       *rels = NIL;
        List       *relids = NIL;
+       List       *relids_extra = NIL;
        List       *relids_logged = NIL;
        ListCell   *cell;
 
@@ -1636,6 +1656,9 @@ ExecuteTruncate(TruncateStmt *stmt)
 
                rels = lappend(rels, rel);
                relids = lappend_oid(relids, myrelid);
+               relids_extra = lappend_int(relids_extra, (recurse ?
+                                                                               
                  TRUNCATE_REL_CONTEXT_NORMAL :
+                                                                               
                  TRUNCATE_REL_CONTEXT_ONLY));
                /* Log this relation only if needed for logical decoding */
                if (RelationIsLogicallyLogged(rel))
                        relids_logged = lappend_oid(relids_logged, myrelid);
@@ -1683,6 +1706,8 @@ ExecuteTruncate(TruncateStmt *stmt)
 
                                rels = lappend(rels, rel);
                                relids = lappend_oid(relids, childrelid);
+                               relids_extra = lappend_int(relids_extra,
+                                                                               
   TRUNCATE_REL_CONTEXT_CASCADING);
                                /* Log this relation only if needed for logical 
decoding */
                                if (RelationIsLogicallyLogged(rel))
                                        relids_logged = 
lappend_oid(relids_logged, childrelid);
@@ -1695,7 +1720,7 @@ ExecuteTruncate(TruncateStmt *stmt)
                                         errhint("Do not specify the ONLY 
keyword, or use TRUNCATE ONLY on the partitions directly.")));
        }
 
-       ExecuteTruncateGuts(rels, relids, relids_logged,
+       ExecuteTruncateGuts(rels, relids, relids_extra, relids_logged,
                                                stmt->behavior, 
stmt->restart_seqs);
 
        /* And close the rels */
@@ -1721,16 +1746,22 @@ ExecuteTruncate(TruncateStmt *stmt)
  * this information handy in this form.
  */
 void
-ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged,
+ExecuteTruncateGuts(List *explicit_rels,
+                                       List *relids,
+                                       List *relids_extra,
+                                       List *relids_logged,
                                        DropBehavior behavior, bool 
restart_seqs)
 {
        List       *rels;
        List       *seq_relids = NIL;
+       HTAB       *ft_htab = NULL;
        EState     *estate;
        ResultRelInfo *resultRelInfos;
        ResultRelInfo *resultRelInfo;
        SubTransactionId mySubid;
        ListCell   *cell;
+       ListCell   *lc1,
+                       *lc2;
        Oid                *logrelids;
 
        /*
@@ -1768,6 +1799,8 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, 
List *relids_logged,
                                truncate_check_activity(rel);
                                rels = lappend(rels, rel);
                                relids = lappend_oid(relids, relid);
+                               relids_extra = lappend_int(relids_extra,
+                                                                               
   TRUNCATE_REL_CONTEXT_CASCADING);
                                /* Log this relation only if needed for logical 
decoding */
                                if (RelationIsLogicallyLogged(rel))
                                        relids_logged = 
lappend_oid(relids_logged, relid);
@@ -1868,14 +1901,63 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, 
List *relids_logged,
         */
        mySubid = GetCurrentSubTransactionId();
 
-       foreach(cell, rels)
+       Assert(list_length(rels) == list_length(relids_extra));
+       forboth(lc1, rels, lc2, relids_extra)
        {
-               Relation        rel = (Relation) lfirst(cell);
+               Relation        rel = (Relation) lfirst(lc1);
+               int                     extra = lfirst_int(lc2);
 
                /* Skip partitioned tables as there is nothing to do */
                if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
                        continue;
 
+               /*
+                * Build the lists of foreign tables belonging to each foreign 
server
+                * and pass each list to the foreign data wrapper's callback 
function,
+                * so that each server can truncate its all foreign tables in 
bulk.
+                * Each list is saved as a single entry in a hash table that 
uses the
+                * server OID as lookup key.
+                */
+               if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+               {
+                       Oid                     serverid = 
GetForeignServerIdByRelId(RelationGetRelid(rel));
+                       bool            found;
+                       ForeignTruncateInfo *ft_info;
+
+                       /* First time through, initialize hashtable for foreign 
tables */
+                       if (!ft_htab)
+                       {
+                               HASHCTL         hctl;
+
+                               memset(&hctl, 0, sizeof(HASHCTL));
+                               hctl.keysize = sizeof(Oid);
+                               hctl.entrysize = sizeof(ForeignTruncateInfo);
+                               hctl.hcxt = CurrentMemoryContext;
+
+                               ft_htab = hash_create("TRUNCATE for Foreign 
Tables",
+                                                                         32,   
/* start small and extend */
+                                                                         &hctl,
+                                                                         
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+                       }
+
+                       /* Find or create cached entry for the foreign table */
+                       ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, 
&found);
+                       if (!found)
+                       {
+                               ft_info->serverid = serverid;
+                               ft_info->rels = NIL;
+                               ft_info->rels_extra = NIL;
+                       }
+
+                       /*
+                        * Save the foreign table in the entry of the server 
that the
+                        * foreign table belongs to.
+                        */
+                       ft_info->rels = lappend(ft_info->rels, rel);
+                       ft_info->rels_extra = lappend_int(ft_info->rels_extra, 
extra);
+                       continue;
+               }
+
                /*
                 * Normally, we need a transaction-safe truncation here.  
However, if
                 * the table was either created in the current (sub)transaction 
or has
@@ -1938,6 +2020,36 @@ ExecuteTruncateGuts(List *explicit_rels, List *relids, 
List *relids_logged,
                pgstat_count_truncate(rel);
        }
 
+       /* Now go through the hash table, and truncate foreign tables */
+       if (ft_htab)
+       {
+               ForeignTruncateInfo *ft_info;
+               HASH_SEQ_STATUS seq;
+
+               hash_seq_init(&seq, ft_htab);
+
+               PG_TRY();
+               {
+                       while ((ft_info = hash_seq_search(&seq)) != NULL)
+                       {
+                               FdwRoutine *routine = 
GetFdwRoutineByServerId(ft_info->serverid);
+
+                               /* truncate_check_rel() has checked that 
already */
+                               Assert(routine->ExecForeignTruncate != NULL);
+
+                               routine->ExecForeignTruncate(ft_info->rels,
+                                                                               
         ft_info->rels_extra,
+                                                                               
         behavior,
+                                                                               
         restart_seqs);
+                       }
+               }
+               PG_FINALLY();
+               {
+                       hash_destroy(ft_htab);
+               }
+               PG_END_TRY();
+       }
+
        /*
         * Restart owned sequences if we were asked to.
         */
@@ -2023,12 +2135,24 @@ truncate_check_rel(Oid relid, Form_pg_class reltuple)
        char       *relname = NameStr(reltuple->relname);
 
        /*
-        * Only allow truncate on regular tables and partitioned tables 
(although,
-        * the latter are only being included here for the following checks; no
-        * physical truncation will occur in their case.)
+        * Only allow truncate on regular tables, foreign tables using foreign
+        * data wrappers supporting TRUNCATE and partitioned tables (although, 
the
+        * latter are only being included here for the following checks; no
+        * physical truncation will occur in their case.).
         */
-       if (reltuple->relkind != RELKIND_RELATION &&
-               reltuple->relkind != RELKIND_PARTITIONED_TABLE)
+       if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
+       {
+               Oid                     serverid = 
GetForeignServerIdByRelId(relid);
+               FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
+
+               if (!fdwroutine->ExecForeignTruncate)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("cannot truncate foreign table 
\"%s\"",
+                                                       relname)));
+       }
+       else if (reltuple->relkind != RELKIND_RELATION &&
+                        reltuple->relkind != RELKIND_PARTITIONED_TABLE)
                ereport(ERROR,
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("\"%s\" is not a table", relname)));
diff --git a/src/backend/replication/logical/worker.c 
b/src/backend/replication/logical/worker.c
index 8da602d163..fb3ba5c415 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -1795,6 +1795,7 @@ apply_handle_truncate(StringInfo s)
        List       *rels = NIL;
        List       *part_rels = NIL;
        List       *relids = NIL;
+       List       *relids_extra = NIL;
        List       *relids_logged = NIL;
        ListCell   *lc;
 
@@ -1824,6 +1825,7 @@ apply_handle_truncate(StringInfo s)
                remote_rels = lappend(remote_rels, rel);
                rels = lappend(rels, rel->localrel);
                relids = lappend_oid(relids, rel->localreloid);
+               relids_extra = lappend_int(relids_extra, 
TRUNCATE_REL_CONTEXT_NORMAL);
                if (RelationIsLogicallyLogged(rel->localrel))
                        relids_logged = lappend_oid(relids_logged, 
rel->localreloid);
 
@@ -1862,6 +1864,7 @@ apply_handle_truncate(StringInfo s)
                                rels = lappend(rels, childrel);
                                part_rels = lappend(part_rels, childrel);
                                relids = lappend_oid(relids, childrelid);
+                               relids_extra = lappend_int(relids_extra, 
TRUNCATE_REL_CONTEXT_CASCADING);
                                /* Log this relation only if needed for logical 
decoding */
                                if (RelationIsLogicallyLogged(childrel))
                                        relids_logged = 
lappend_oid(relids_logged, childrelid);
@@ -1874,8 +1877,12 @@ apply_handle_truncate(StringInfo s)
         * to replaying changes without further cascading. This might be later
         * changeable with a user specified option.
         */
-       ExecuteTruncateGuts(rels, relids, relids_logged, DROP_RESTRICT, 
restart_seqs);
-
+       ExecuteTruncateGuts(rels,
+                                               relids,
+                                               relids_extra,
+                                               relids_logged,
+                                               DROP_RESTRICT,
+                                               restart_seqs);
        foreach(lc, remote_rels)
        {
                LogicalRepRelMapEntry *rel = lfirst(lc);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index b3d30acc35..57763ed734 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -55,9 +55,16 @@ extern void AlterRelationNamespaceInternal(Relation 
classRel, Oid relOid,
 
 extern void CheckTableNotInUse(Relation rel, const char *stmt);
 
+#define TRUNCATE_REL_CONTEXT_NORMAL       0x01
+#define TRUNCATE_REL_CONTEXT_ONLY         0x02
+#define TRUNCATE_REL_CONTEXT_CASCADING     0x04
 extern void ExecuteTruncate(TruncateStmt *stmt);
-extern void ExecuteTruncateGuts(List *explicit_rels, List *relids, List 
*relids_logged,
-                                                               DropBehavior 
behavior, bool restart_seqs);
+extern void ExecuteTruncateGuts(List *explicit_rels,
+                                                               List *relids,
+                                                               List 
*relids_extra,
+                                                               List 
*relids_logged,
+                                                               DropBehavior 
behavior,
+                                                               bool 
restart_seqs);
 
 extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
 
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 10d29ff292..4ebbca6de9 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -160,6 +160,11 @@ typedef bool (*AnalyzeForeignTable_function) (Relation 
relation,
 typedef List *(*ImportForeignSchema_function) (ImportForeignSchemaStmt *stmt,
                                                                                
           Oid serverOid);
 
+typedef void (*ExecForeignTruncate_function) (List *rels,
+                                                                               
          List *rels_extra,
+                                                                               
          DropBehavior behavior,
+                                                                               
          bool restart_seqs);
+
 typedef Size (*EstimateDSMForeignScan_function) (ForeignScanState *node,
                                                                                
                 ParallelContext *pcxt);
 typedef void (*InitializeDSMForeignScan_function) (ForeignScanState *node,
@@ -255,6 +260,9 @@ typedef struct FdwRoutine
        /* Support functions for IMPORT FOREIGN SCHEMA */
        ImportForeignSchema_function ImportForeignSchema;
 
+       /* Support functions for TRUNCATE */
+       ExecForeignTruncate_function ExecForeignTruncate;
+
        /* Support functions for parallelism under Gather node */
        IsForeignScanParallelSafe_function IsForeignScanParallelSafe;
        EstimateDSMForeignScan_function EstimateDSMForeignScan;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 9a3a03e520..34e25eb597 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -634,7 +634,8 @@ typedef struct ViewOptions
  *             WAL stream.
  *
  * We don't log information for unlogged tables (since they don't WAL log
- * anyway) and for system tables (their content is hard to make sense of, and
+ * anyway), for foreign tables (since they don't WAL log, either),
+ * and for system tables (their content is hard to make sense of, and
  * it would complicate decoding slightly for little gain). Note that we *do*
  * log information for user defined catalog tables since they presumably are
  * interesting to the user...
@@ -642,6 +643,7 @@ typedef struct ViewOptions
 #define RelationIsLogicallyLogged(relation) \
        (XLogLogicalInfoActive() && \
         RelationNeedsWAL(relation) && \
+        (relation)->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&        \
         !IsCatalogRelation(relation))
 
 /* routines in utils/cache/relcache.c */
diff --git a/src/test/regress/expected/foreign_data.out 
b/src/test/regress/expected/foreign_data.out
index e4cdb780d0..5385f98a0f 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1807,9 +1807,9 @@ Inherits: fd_pt1
 
 -- TRUNCATE doesn't work on foreign tables, either directly or recursively
 TRUNCATE ft2;  -- ERROR
-ERROR:  "ft2" is not a table
+ERROR:  foreign-data wrapper "dummy" has no handler
 TRUNCATE fd_pt1;  -- ERROR
-ERROR:  "ft2" is not a table
+ERROR:  foreign-data wrapper "dummy" has no handler
 DROP TABLE fd_pt1 CASCADE;
 NOTICE:  drop cascades to foreign table ft2
 -- IMPORT FOREIGN SCHEMA
@@ -2032,9 +2032,9 @@ ALTER FOREIGN TABLE fd_pt2_1 ADD CONSTRAINT fd_pt2chk1 
CHECK (c1 > 0);
 ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1);
 -- TRUNCATE doesn't work on foreign tables, either directly or recursively
 TRUNCATE fd_pt2_1;  -- ERROR
-ERROR:  "fd_pt2_1" is not a table
+ERROR:  foreign-data wrapper "dummy" has no handler
 TRUNCATE fd_pt2;  -- ERROR
-ERROR:  "fd_pt2_1" is not a table
+ERROR:  foreign-data wrapper "dummy" has no handler
 DROP FOREIGN TABLE fd_pt2_1;
 DROP TABLE fd_pt2;
 -- foreign table cannot be part of partition tree made of temporary
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index efb9811198..fefa65705a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -704,6 +704,7 @@ ForeignScanState
 ForeignServer
 ForeignServerInfo
 ForeignTable
+ForeignTruncateInfo
 ForkNumber
 FormData_pg_aggregate
 FormData_pg_am

Reply via email to