16
16
#include <unistd.h>
17
17
18
18
#include "access/reloptions.h"
19
+ #include "access/sysattr.h"
19
20
#include "catalog/pg_foreign_table.h"
20
21
#include "commands/copy.h"
21
22
#include "commands/defrem.h"
29
30
#include "optimizer/pathnode.h"
30
31
#include "optimizer/planmain.h"
31
32
#include "optimizer/restrictinfo.h"
33
+ #include "optimizer/var.h"
32
34
#include "utils/memutils.h"
33
35
#include "utils/rel.h"
34
36
@@ -136,6 +138,9 @@ static bool is_valid_option(const char *option, Oid context);
136
138
static void fileGetOptions (Oid foreigntableid ,
137
139
char * * filename , List * * other_options );
138
140
static List * get_file_fdw_attribute_options (Oid relid );
141
+ static bool check_selective_binary_conversion (RelOptInfo * baserel ,
142
+ Oid foreigntableid ,
143
+ List * * columns );
139
144
static void estimate_size (PlannerInfo * root , RelOptInfo * baserel ,
140
145
FileFdwPlanState * fdw_private );
141
146
static void estimate_costs (PlannerInfo * root , RelOptInfo * baserel ,
@@ -457,20 +462,33 @@ fileGetForeignPaths(PlannerInfo *root,
457
462
FileFdwPlanState * fdw_private = (FileFdwPlanState * ) baserel -> fdw_private ;
458
463
Cost startup_cost ;
459
464
Cost 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 ));
460
474
461
475
/* Estimate costs */
462
476
estimate_costs (root , baserel , fdw_private ,
463
477
& startup_cost , & total_cost );
464
478
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
+ */
466
484
add_path (baserel , (Path * )
467
485
create_foreignscan_path (root , baserel ,
468
486
baserel -> rows ,
469
487
startup_cost ,
470
488
total_cost ,
471
489
NIL , /* no pathkeys */
472
490
NULL , /* no outer rel either */
473
- NIL )); /* no fdw_private data */
491
+ coptions ));
474
492
475
493
/*
476
494
* If data file was sorted, and we knew it somehow, we could insert
@@ -507,7 +525,7 @@ fileGetForeignPlan(PlannerInfo *root,
507
525
scan_clauses ,
508
526
scan_relid ,
509
527
NIL , /* no expressions to evaluate */
510
- NIL ); /* no private state either */
528
+ best_path -> fdw_private );
511
529
}
512
530
513
531
/*
@@ -544,6 +562,7 @@ fileExplainForeignScan(ForeignScanState *node, ExplainState *es)
544
562
static void
545
563
fileBeginForeignScan (ForeignScanState * node , int eflags )
546
564
{
565
+ ForeignScan * plan = (ForeignScan * ) node -> ss .ps .plan ;
547
566
char * filename ;
548
567
List * options ;
549
568
CopyState cstate ;
@@ -559,6 +578,9 @@ fileBeginForeignScan(ForeignScanState *node, int eflags)
559
578
fileGetOptions (RelationGetRelid (node -> ss .ss_currentRelation ),
560
579
& filename , & options );
561
580
581
+ /* Add any options from the plan (currently only convert_selectively) */
582
+ options = list_concat (options , plan -> fdw_private );
583
+
562
584
/*
563
585
* Create CopyState from FDW options. We always acquire all columns, so
564
586
* as to match the expected ScanTupleSlot signature.
@@ -694,6 +716,125 @@ fileAnalyzeForeignTable(Relation relation,
694
716
return true;
695
717
}
696
718
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
+
697
838
/*
698
839
* Estimate size of a foreign table.
699
840
*
0 commit comments