@@ -254,7 +254,8 @@ typedef struct PgFdwAnalyzeState
254
254
typedef struct ConversionLocation
255
255
{
256
256
AttrNumber cur_attno ; /* attribute number being processed, or 0 */
257
- ForeignScanState * fsstate ; /* plan node being processed */
257
+ Relation rel ; /* foreign table being processed, or NULL */
258
+ ForeignScanState * fsstate ; /* plan node being processed, or NULL */
258
259
} ConversionLocation ;
259
260
260
261
/* Callback argument for ec_member_matches_foreign */
@@ -5639,7 +5640,12 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
5639
5640
* rel is the local representation of the foreign table, attinmeta is
5640
5641
* conversion data for the rel's tupdesc, and retrieved_attrs is an
5641
5642
* integer list of the table column numbers present in the PGresult.
5643
+ * fsstate is the ForeignScan plan node's execution state.
5642
5644
* temp_context is a working context that can be reset after each tuple.
5645
+ *
5646
+ * Note: either rel or fsstate, but not both, can be NULL. rel is NULL
5647
+ * if we're processing a remote join, while fsstate is NULL in a non-query
5648
+ * context such as ANALYZE, or if we're processing a non-scan query node.
5643
5649
*/
5644
5650
static HeapTuple
5645
5651
make_tuple_from_result_row (PGresult * res ,
@@ -5671,6 +5677,10 @@ make_tuple_from_result_row(PGresult *res,
5671
5677
*/
5672
5678
oldcontext = MemoryContextSwitchTo (temp_context );
5673
5679
5680
+ /*
5681
+ * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
5682
+ * provided, otherwise look to the scan node's ScanTupleSlot.
5683
+ */
5674
5684
if (rel )
5675
5685
tupdesc = RelationGetDescr (rel );
5676
5686
else
@@ -5688,6 +5698,7 @@ make_tuple_from_result_row(PGresult *res,
5688
5698
* Set up and install callback to report where conversion error occurs.
5689
5699
*/
5690
5700
errpos .cur_attno = 0 ;
5701
+ errpos .rel = rel ;
5691
5702
errpos .fsstate = fsstate ;
5692
5703
errcallback .callback = conversion_error_callback ;
5693
5704
errcallback .arg = (void * ) & errpos ;
@@ -5809,63 +5820,90 @@ make_tuple_from_result_row(PGresult *res,
5809
5820
*
5810
5821
* Note that this function mustn't do any catalog lookups, since we are in
5811
5822
* an already-failed transaction. Fortunately, we can get the needed info
5812
- * from the query's rangetable instead.
5823
+ * from the relation or the query's rangetable instead.
5813
5824
*/
5814
5825
static void
5815
5826
conversion_error_callback (void * arg )
5816
5827
{
5817
5828
ConversionLocation * errpos = (ConversionLocation * ) arg ;
5829
+ Relation rel = errpos -> rel ;
5818
5830
ForeignScanState * fsstate = errpos -> fsstate ;
5819
- ForeignScan * fsplan = castNode (ForeignScan , fsstate -> ss .ps .plan );
5820
- int varno = 0 ;
5821
- AttrNumber colno = 0 ;
5822
5831
const char * attname = NULL ;
5823
5832
const char * relname = NULL ;
5824
5833
bool is_wholerow = false;
5825
5834
5826
- if (fsplan -> scan .scanrelid > 0 )
5827
- {
5828
- /* error occurred in a scan against a foreign table */
5829
- varno = fsplan -> scan .scanrelid ;
5830
- colno = errpos -> cur_attno ;
5831
- }
5832
- else
5835
+ /*
5836
+ * If we're in a scan node, always use aliases from the rangetable, for
5837
+ * consistency between the simple-relation and remote-join cases. Look at
5838
+ * the relation's tupdesc only if we're not in a scan node.
5839
+ */
5840
+ if (fsstate )
5833
5841
{
5834
- /* error occurred in a scan against a foreign join */
5835
- TargetEntry * tle ;
5836
-
5837
- tle = list_nth_node (TargetEntry , fsplan -> fdw_scan_tlist ,
5838
- errpos -> cur_attno - 1 );
5842
+ /* ForeignScan case */
5843
+ ForeignScan * fsplan = castNode (ForeignScan , fsstate -> ss .ps .plan );
5844
+ int varno = 0 ;
5845
+ AttrNumber colno = 0 ;
5839
5846
5840
- /*
5841
- * Target list can have Vars and expressions. For Vars, we can get
5842
- * some information, however for expressions we can't. Thus for
5843
- * expressions, just show generic context message.
5844
- */
5845
- if (IsA (tle -> expr , Var ))
5847
+ if (fsplan -> scan .scanrelid > 0 )
5848
+ {
5849
+ /* error occurred in a scan against a foreign table */
5850
+ varno = fsplan -> scan .scanrelid ;
5851
+ colno = errpos -> cur_attno ;
5852
+ }
5853
+ else
5846
5854
{
5847
- Var * var = (Var * ) tle -> expr ;
5855
+ /* error occurred in a scan against a foreign join */
5856
+ TargetEntry * tle ;
5857
+
5858
+ tle = list_nth_node (TargetEntry , fsplan -> fdw_scan_tlist ,
5859
+ errpos -> cur_attno - 1 );
5860
+
5861
+ /*
5862
+ * Target list can have Vars and expressions. For Vars, we can
5863
+ * get some information, however for expressions we can't. Thus
5864
+ * for expressions, just show generic context message.
5865
+ */
5866
+ if (IsA (tle -> expr , Var ))
5867
+ {
5868
+ Var * var = (Var * ) tle -> expr ;
5869
+
5870
+ varno = var -> varno ;
5871
+ colno = var -> varattno ;
5872
+ }
5873
+ }
5848
5874
5849
- varno = var -> varno ;
5850
- colno = var -> varattno ;
5875
+ if (varno > 0 )
5876
+ {
5877
+ EState * estate = fsstate -> ss .ps .state ;
5878
+ RangeTblEntry * rte = rt_fetch (varno , estate -> es_range_table );
5879
+
5880
+ relname = rte -> eref -> aliasname ;
5881
+
5882
+ if (colno == 0 )
5883
+ is_wholerow = true;
5884
+ else if (colno > 0 && colno <= list_length (rte -> eref -> colnames ))
5885
+ attname = strVal (list_nth (rte -> eref -> colnames , colno - 1 ));
5886
+ else if (colno == SelfItemPointerAttributeNumber )
5887
+ attname = "ctid" ;
5888
+ else if (colno == ObjectIdAttributeNumber )
5889
+ attname = "oid" ;
5851
5890
}
5852
5891
}
5853
-
5854
- if (varno > 0 )
5892
+ else if (rel )
5855
5893
{
5856
- EState * estate = fsstate -> ss . ps . state ;
5857
- RangeTblEntry * rte = rt_fetch ( varno , estate -> es_range_table );
5894
+ /* Non-ForeignScan case (we should always have a rel here) */
5895
+ TupleDesc tupdesc = RelationGetDescr ( rel );
5858
5896
5859
- relname = rte -> eref -> aliasname ;
5897
+ relname = RelationGetRelationName (rel );
5898
+ if (errpos -> cur_attno > 0 && errpos -> cur_attno <= tupdesc -> natts )
5899
+ {
5900
+ Form_pg_attribute attr = TupleDescAttr (tupdesc ,
5901
+ errpos -> cur_attno - 1 );
5860
5902
5861
- if (colno == 0 )
5862
- is_wholerow = true;
5863
- else if (colno > 0 && colno <= list_length (rte -> eref -> colnames ))
5864
- attname = strVal (list_nth (rte -> eref -> colnames , colno - 1 ));
5865
- else if (colno == SelfItemPointerAttributeNumber )
5903
+ attname = NameStr (attr -> attname );
5904
+ }
5905
+ else if (errpos -> cur_attno == SelfItemPointerAttributeNumber )
5866
5906
attname = "ctid" ;
5867
- else if (colno == ObjectIdAttributeNumber )
5868
- attname = "oid" ;
5869
5907
}
5870
5908
5871
5909
if (relname && is_wholerow )
0 commit comments