1616#include <unistd.h>
1717
1818#include "access/reloptions.h"
19+ #include "access/sysattr.h"
1920#include "catalog/pg_foreign_table.h"
2021#include "commands/copy.h"
2122#include "commands/defrem.h"
2930#include "optimizer/pathnode.h"
3031#include "optimizer/planmain.h"
3132#include "optimizer/restrictinfo.h"
33+ #include "optimizer/var.h"
3234#include "utils/memutils.h"
3335#include "utils/rel.h"
3436
@@ -136,6 +138,9 @@ static bool is_valid_option(const char *option, Oid context);
136138static void fileGetOptions (Oid foreigntableid ,
137139char * * filename ,List * * other_options );
138140static List * get_file_fdw_attribute_options (Oid relid );
141+ static bool check_selective_binary_conversion (RelOptInfo * baserel ,
142+ Oid foreigntableid ,
143+ List * * columns );
139144static void estimate_size (PlannerInfo * root ,RelOptInfo * baserel ,
140145FileFdwPlanState * fdw_private );
141146static void estimate_costs (PlannerInfo * root ,RelOptInfo * baserel ,
@@ -457,20 +462,33 @@ fileGetForeignPaths(PlannerInfo *root,
457462FileFdwPlanState * fdw_private = (FileFdwPlanState * )baserel -> fdw_private ;
458463Cost startup_cost ;
459464Cost total_cost ;
465+ List * columns ;
466+ List * coptions = NIL ;
467+
468+ /* Decide whether to selectively perform binary conversion */
469+ if (check_selective_binary_conversion (baserel ,
470+ foreigntableid ,
471+ & columns ))
472+ coptions = list_make1 (makeDefElem ("convert_selectively" ,
473+ (Node * )columns ));
460474
461475/* Estimate costs */
462476estimate_costs (root ,baserel ,fdw_private ,
463477& startup_cost ,& total_cost );
464478
465- /* Create a ForeignPath node and add it as only possible path */
479+ /*
480+ * Create a ForeignPath node and add it as only possible path. We use the
481+ * fdw_private list of the path to carry the convert_selectively option;
482+ * it will be propagated into the fdw_private list of the Plan node.
483+ */
466484add_path (baserel , (Path * )
467485create_foreignscan_path (root ,baserel ,
468486baserel -> rows ,
469487startup_cost ,
470488total_cost ,
471489NIL ,/* no pathkeys */
472490NULL ,/* no outer rel either */
473- NIL ));/* no fdw_private data */
491+ coptions ));
474492
475493/*
476494 * If data file was sorted, and we knew it somehow, we could insert
@@ -507,7 +525,7 @@ fileGetForeignPlan(PlannerInfo *root,
507525scan_clauses ,
508526scan_relid ,
509527NIL ,/* no expressions to evaluate */
510- NIL ); /* no private state either */
528+ best_path -> fdw_private );
511529}
512530
513531/*
@@ -544,6 +562,7 @@ fileExplainForeignScan(ForeignScanState *node, ExplainState *es)
544562static void
545563fileBeginForeignScan (ForeignScanState * node ,int eflags )
546564{
565+ ForeignScan * plan = (ForeignScan * )node -> ss .ps .plan ;
547566char * filename ;
548567List * options ;
549568CopyState cstate ;
@@ -559,6 +578,9 @@ fileBeginForeignScan(ForeignScanState *node, int eflags)
559578fileGetOptions (RelationGetRelid (node -> ss .ss_currentRelation ),
560579& filename ,& options );
561580
581+ /* Add any options from the plan (currently only convert_selectively) */
582+ options = list_concat (options ,plan -> fdw_private );
583+
562584/*
563585 * Create CopyState from FDW options. We always acquire all columns, so
564586 * as to match the expected ScanTupleSlot signature.
@@ -694,6 +716,125 @@ fileAnalyzeForeignTable(Relation relation,
694716return true;
695717}
696718
719+ /*
720+ * check_selective_binary_conversion
721+ *
722+ * Check to see if it's useful to convert only a subset of the file's columns
723+ * to binary. If so, construct a list of the column names to be converted,
724+ * return that at *columns, and return TRUE. (Note that it's possible to
725+ * determine that no columns need be converted, for instance with a COUNT(*)
726+ * query. So we can't use returning a NIL list to indicate failure.)
727+ */
728+ static bool
729+ check_selective_binary_conversion (RelOptInfo * baserel ,
730+ Oid foreigntableid ,
731+ List * * columns )
732+ {
733+ ForeignTable * table ;
734+ ListCell * lc ;
735+ Relation rel ;
736+ TupleDesc tupleDesc ;
737+ AttrNumber attnum ;
738+ Bitmapset * attrs_used = NULL ;
739+ bool has_wholerow = false;
740+ int numattrs ;
741+ int i ;
742+
743+ * columns = NIL ;/* default result */
744+
745+ /*
746+ * Check format of the file. If binary format, this is irrelevant.
747+ */
748+ table = GetForeignTable (foreigntableid );
749+ foreach (lc ,table -> options )
750+ {
751+ DefElem * def = (DefElem * )lfirst (lc );
752+
753+ if (strcmp (def -> defname ,"format" )== 0 )
754+ {
755+ char * format = defGetString (def );
756+
757+ if (strcmp (format ,"binary" )== 0 )
758+ return false;
759+ break ;
760+ }
761+ }
762+
763+ /* Collect all the attributes needed for joins or final output. */
764+ pull_varattnos ((Node * )baserel -> reltargetlist ,baserel -> relid ,
765+ & attrs_used );
766+
767+ /* Add all the attributes used by restriction clauses. */
768+ foreach (lc ,baserel -> baserestrictinfo )
769+ {
770+ RestrictInfo * rinfo = (RestrictInfo * )lfirst (lc );
771+
772+ pull_varattnos ((Node * )rinfo -> clause ,baserel -> relid ,
773+ & attrs_used );
774+ }
775+
776+ /* Convert attribute numbers to column names. */
777+ rel = heap_open (foreigntableid ,AccessShareLock );
778+ tupleDesc = RelationGetDescr (rel );
779+
780+ while ((attnum = bms_first_member (attrs_used )) >=0 )
781+ {
782+ /* Adjust for system attributes. */
783+ attnum += FirstLowInvalidHeapAttributeNumber ;
784+
785+ if (attnum == 0 )
786+ {
787+ has_wholerow = true;
788+ break ;
789+ }
790+
791+ /* Ignore system attributes. */
792+ if (attnum < 0 )
793+ continue ;
794+
795+ /* Get user attributes. */
796+ if (attnum > 0 )
797+ {
798+ Form_pg_attribute attr = tupleDesc -> attrs [attnum - 1 ];
799+ char * attname = NameStr (attr -> attname );
800+
801+ /* Skip dropped attributes (probably shouldn't see any here). */
802+ if (attr -> attisdropped )
803+ continue ;
804+ * columns = lappend (* columns ,makeString (pstrdup (attname )));
805+ }
806+ }
807+
808+ /* Count non-dropped user attributes while we have the tupdesc. */
809+ numattrs = 0 ;
810+ for (i = 0 ;i < tupleDesc -> natts ;i ++ )
811+ {
812+ Form_pg_attribute attr = tupleDesc -> attrs [i ];
813+
814+ if (attr -> attisdropped )
815+ continue ;
816+ numattrs ++ ;
817+ }
818+
819+ heap_close (rel ,AccessShareLock );
820+
821+ /* If there's a whole-row reference, fail: we need all the columns. */
822+ if (has_wholerow )
823+ {
824+ * columns = NIL ;
825+ return false;
826+ }
827+
828+ /* If all the user attributes are needed, fail. */
829+ if (numattrs == list_length (* columns ))
830+ {
831+ * columns = NIL ;
832+ return false;
833+ }
834+
835+ return true;
836+ }
837+
697838/*
698839 * Estimate size of a foreign table.
699840 *