@@ -244,7 +244,8 @@ typedef struct PgFdwAnalyzeState
244
244
typedef struct ConversionLocation
245
245
{
246
246
AttrNumber cur_attno ; /* attribute number being processed, or 0 */
247
- ForeignScanState * fsstate ; /* plan node being processed */
247
+ Relation rel ; /* foreign table being processed, or NULL */
248
+ ForeignScanState * fsstate ; /* plan node being processed, or NULL */
248
249
} ConversionLocation ;
249
250
250
251
/* Callback argument for ec_member_matches_foreign */
@@ -4396,7 +4397,12 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
4396
4397
* rel is the local representation of the foreign table, attinmeta is
4397
4398
* conversion data for the rel's tupdesc, and retrieved_attrs is an
4398
4399
* integer list of the table column numbers present in the PGresult.
4400
+ * fsstate is the ForeignScan plan node's execution state.
4399
4401
* temp_context is a working context that can be reset after each tuple.
4402
+ *
4403
+ * Note: either rel or fsstate, but not both, can be NULL. rel is NULL
4404
+ * if we're processing a remote join, while fsstate is NULL in a non-query
4405
+ * context such as ANALYZE, or if we're processing a non-scan query node.
4400
4406
*/
4401
4407
static HeapTuple
4402
4408
make_tuple_from_result_row (PGresult * res ,
@@ -4427,6 +4433,10 @@ make_tuple_from_result_row(PGresult *res,
4427
4433
*/
4428
4434
oldcontext = MemoryContextSwitchTo (temp_context );
4429
4435
4436
+ /*
4437
+ * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
4438
+ * provided, otherwise look to the scan node's ScanTupleSlot.
4439
+ */
4430
4440
if (rel )
4431
4441
tupdesc = RelationGetDescr (rel );
4432
4442
else
@@ -4447,6 +4457,7 @@ make_tuple_from_result_row(PGresult *res,
4447
4457
* Set up and install callback to report where conversion error occurs.
4448
4458
*/
4449
4459
errpos .cur_attno = 0 ;
4460
+ errpos .rel = rel ;
4450
4461
errpos .fsstate = fsstate ;
4451
4462
errcallback .callback = conversion_error_callback ;
4452
4463
errcallback .arg = (void * ) & errpos ;
@@ -4547,65 +4558,92 @@ make_tuple_from_result_row(PGresult *res,
4547
4558
*
4548
4559
* Note that this function mustn't do any catalog lookups, since we are in
4549
4560
* an already-failed transaction. Fortunately, we can get the needed info
4550
- * from the query's rangetable instead.
4561
+ * from the relation or the query's rangetable instead.
4551
4562
*/
4552
4563
static void
4553
4564
conversion_error_callback (void * arg )
4554
4565
{
4555
4566
ConversionLocation * errpos = (ConversionLocation * ) arg ;
4567
+ Relation rel = errpos -> rel ;
4556
4568
ForeignScanState * fsstate = errpos -> fsstate ;
4557
- ForeignScan * fsplan = (ForeignScan * ) fsstate -> ss .ps .plan ;
4558
- int varno = 0 ;
4559
- AttrNumber colno = 0 ;
4560
4569
const char * attname = NULL ;
4561
4570
const char * relname = NULL ;
4562
4571
bool is_wholerow = false;
4563
4572
4564
- if (fsplan -> scan .scanrelid > 0 )
4565
- {
4566
- /* error occurred in a scan against a foreign table */
4567
- varno = fsplan -> scan .scanrelid ;
4568
- colno = errpos -> cur_attno ;
4569
- }
4570
- else
4573
+ /*
4574
+ * If we're in a scan node, always use aliases from the rangetable, for
4575
+ * consistency between the simple-relation and remote-join cases. Look at
4576
+ * the relation's tupdesc only if we're not in a scan node.
4577
+ */
4578
+ if (fsstate )
4571
4579
{
4572
- /* error occurred in a scan against a foreign join */
4573
- TargetEntry * tle ;
4574
-
4575
- Assert (IsA (fsplan , ForeignScan ));
4576
- tle = (TargetEntry * ) list_nth (fsplan -> fdw_scan_tlist ,
4577
- errpos -> cur_attno - 1 );
4578
- Assert (IsA (tle , TargetEntry ));
4580
+ /* ForeignScan case */
4581
+ ForeignScan * fsplan = (ForeignScan * ) fsstate -> ss .ps .plan ;
4582
+ int varno = 0 ;
4583
+ AttrNumber colno = 0 ;
4579
4584
4580
- /*
4581
- * Target list can have Vars and expressions. For Vars, we can get
4582
- * some information, however for expressions we can't. Thus for
4583
- * expressions, just show generic context message.
4584
- */
4585
- if (IsA (tle -> expr , Var ))
4585
+ if (fsplan -> scan .scanrelid > 0 )
4586
+ {
4587
+ /* error occurred in a scan against a foreign table */
4588
+ varno = fsplan -> scan .scanrelid ;
4589
+ colno = errpos -> cur_attno ;
4590
+ }
4591
+ else
4586
4592
{
4587
- Var * var = (Var * ) tle -> expr ;
4593
+ /* error occurred in a scan against a foreign join */
4594
+ TargetEntry * tle ;
4595
+
4596
+ Assert (IsA (fsplan , ForeignScan ));
4597
+ tle = (TargetEntry * ) list_nth (fsplan -> fdw_scan_tlist ,
4598
+ errpos -> cur_attno - 1 );
4599
+ Assert (IsA (tle , TargetEntry ));
4600
+
4601
+ /*
4602
+ * Target list can have Vars and expressions. For Vars, we can
4603
+ * get some information, however for expressions we can't. Thus
4604
+ * for expressions, just show generic context message.
4605
+ */
4606
+ if (IsA (tle -> expr , Var ))
4607
+ {
4608
+ Var * var = (Var * ) tle -> expr ;
4609
+
4610
+ varno = var -> varno ;
4611
+ colno = var -> varattno ;
4612
+ }
4613
+ }
4588
4614
4589
- varno = var -> varno ;
4590
- colno = var -> varattno ;
4615
+ if (varno > 0 )
4616
+ {
4617
+ EState * estate = fsstate -> ss .ps .state ;
4618
+ RangeTblEntry * rte = rt_fetch (varno , estate -> es_range_table );
4619
+
4620
+ relname = rte -> eref -> aliasname ;
4621
+
4622
+ if (colno == 0 )
4623
+ is_wholerow = true;
4624
+ else if (colno > 0 && colno <= list_length (rte -> eref -> colnames ))
4625
+ attname = strVal (list_nth (rte -> eref -> colnames , colno - 1 ));
4626
+ else if (colno == SelfItemPointerAttributeNumber )
4627
+ attname = "ctid" ;
4628
+ else if (colno == ObjectIdAttributeNumber )
4629
+ attname = "oid" ;
4591
4630
}
4592
4631
}
4593
-
4594
- if (varno > 0 )
4632
+ else if (rel )
4595
4633
{
4596
- EState * estate = fsstate -> ss . ps . state ;
4597
- RangeTblEntry * rte = rt_fetch ( varno , estate -> es_range_table );
4634
+ /* Non-ForeignScan case (we should always have a rel here) */
4635
+ TupleDesc tupdesc = RelationGetDescr ( rel );
4598
4636
4599
- relname = rte -> eref -> aliasname ;
4637
+ relname = RelationGetRelationName (rel );
4638
+ if (errpos -> cur_attno > 0 && errpos -> cur_attno <= tupdesc -> natts )
4639
+ {
4640
+ Form_pg_attribute attr = TupleDescAttr (tupdesc ,
4641
+ errpos -> cur_attno - 1 );
4600
4642
4601
- if (colno == 0 )
4602
- is_wholerow = true;
4603
- else if (colno > 0 && colno <= list_length (rte -> eref -> colnames ))
4604
- attname = strVal (list_nth (rte -> eref -> colnames , colno - 1 ));
4605
- else if (colno == SelfItemPointerAttributeNumber )
4643
+ attname = NameStr (attr -> attname );
4644
+ }
4645
+ else if (errpos -> cur_attno == SelfItemPointerAttributeNumber )
4606
4646
attname = "ctid" ;
4607
- else if (colno == ObjectIdAttributeNumber )
4608
- attname = "oid" ;
4609
4647
}
4610
4648
4611
4649
if (relname && is_wholerow )
0 commit comments