@@ -245,7 +245,8 @@ typedef struct PgFdwAnalyzeState
245
245
typedef struct ConversionLocation
246
246
{
247
247
AttrNumber cur_attno ; /* attribute number being processed, or 0 */
248
- ForeignScanState * fsstate ; /* plan node being processed */
248
+ Relation rel ; /* foreign table being processed, or NULL */
249
+ ForeignScanState * fsstate ; /* plan node being processed, or NULL */
249
250
} ConversionLocation ;
250
251
251
252
/* Callback argument for ec_member_matches_foreign */
@@ -4967,7 +4968,12 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
4967
4968
* rel is the local representation of the foreign table, attinmeta is
4968
4969
* conversion data for the rel's tupdesc, and retrieved_attrs is an
4969
4970
* integer list of the table column numbers present in the PGresult.
4971
+ * fsstate is the ForeignScan plan node's execution state.
4970
4972
* temp_context is a working context that can be reset after each tuple.
4973
+ *
4974
+ * Note: either rel or fsstate, but not both, can be NULL. rel is NULL
4975
+ * if we're processing a remote join, while fsstate is NULL in a non-query
4976
+ * context such as ANALYZE, or if we're processing a non-scan query node.
4971
4977
*/
4972
4978
static HeapTuple
4973
4979
make_tuple_from_result_row (PGresult * res ,
@@ -4999,6 +5005,10 @@ make_tuple_from_result_row(PGresult *res,
4999
5005
*/
5000
5006
oldcontext = MemoryContextSwitchTo (temp_context );
5001
5007
5008
+ /*
5009
+ * Get the tuple descriptor for the row. Use the rel's tupdesc if rel is
5010
+ * provided, otherwise look to the scan node's ScanTupleSlot.
5011
+ */
5002
5012
if (rel )
5003
5013
tupdesc = RelationGetDescr (rel );
5004
5014
else
@@ -5019,6 +5029,7 @@ make_tuple_from_result_row(PGresult *res,
5019
5029
* Set up and install callback to report where conversion error occurs.
5020
5030
*/
5021
5031
errpos .cur_attno = 0 ;
5032
+ errpos .rel = rel ;
5022
5033
errpos .fsstate = fsstate ;
5023
5034
errcallback .callback = conversion_error_callback ;
5024
5035
errcallback .arg = (void * ) & errpos ;
@@ -5140,63 +5151,90 @@ make_tuple_from_result_row(PGresult *res,
5140
5151
*
5141
5152
* Note that this function mustn't do any catalog lookups, since we are in
5142
5153
* an already-failed transaction. Fortunately, we can get the needed info
5143
- * from the query's rangetable instead.
5154
+ * from the relation or the query's rangetable instead.
5144
5155
*/
5145
5156
static void
5146
5157
conversion_error_callback (void * arg )
5147
5158
{
5148
5159
ConversionLocation * errpos = (ConversionLocation * ) arg ;
5160
+ Relation rel = errpos -> rel ;
5149
5161
ForeignScanState * fsstate = errpos -> fsstate ;
5150
- ForeignScan * fsplan = castNode (ForeignScan , fsstate -> ss .ps .plan );
5151
- int varno = 0 ;
5152
- AttrNumber colno = 0 ;
5153
5162
const char * attname = NULL ;
5154
5163
const char * relname = NULL ;
5155
5164
bool is_wholerow = false;
5156
5165
5157
- if (fsplan -> scan .scanrelid > 0 )
5158
- {
5159
- /* error occurred in a scan against a foreign table */
5160
- varno = fsplan -> scan .scanrelid ;
5161
- colno = errpos -> cur_attno ;
5162
- }
5163
- else
5166
+ /*
5167
+ * If we're in a scan node, always use aliases from the rangetable, for
5168
+ * consistency between the simple-relation and remote-join cases. Look at
5169
+ * the relation's tupdesc only if we're not in a scan node.
5170
+ */
5171
+ if (fsstate )
5164
5172
{
5165
- /* error occurred in a scan against a foreign join */
5166
- TargetEntry * tle ;
5167
-
5168
- tle = list_nth_node (TargetEntry , fsplan -> fdw_scan_tlist ,
5169
- errpos -> cur_attno - 1 );
5173
+ /* ForeignScan case */
5174
+ ForeignScan * fsplan = castNode (ForeignScan , fsstate -> ss .ps .plan );
5175
+ int varno = 0 ;
5176
+ AttrNumber colno = 0 ;
5170
5177
5171
- /*
5172
- * Target list can have Vars and expressions. For Vars, we can get
5173
- * some information, however for expressions we can't. Thus for
5174
- * expressions, just show generic context message.
5175
- */
5176
- if (IsA (tle -> expr , Var ))
5178
+ if (fsplan -> scan .scanrelid > 0 )
5179
+ {
5180
+ /* error occurred in a scan against a foreign table */
5181
+ varno = fsplan -> scan .scanrelid ;
5182
+ colno = errpos -> cur_attno ;
5183
+ }
5184
+ else
5177
5185
{
5178
- Var * var = (Var * ) tle -> expr ;
5186
+ /* error occurred in a scan against a foreign join */
5187
+ TargetEntry * tle ;
5188
+
5189
+ tle = list_nth_node (TargetEntry , fsplan -> fdw_scan_tlist ,
5190
+ errpos -> cur_attno - 1 );
5191
+
5192
+ /*
5193
+ * Target list can have Vars and expressions. For Vars, we can
5194
+ * get some information, however for expressions we can't. Thus
5195
+ * for expressions, just show generic context message.
5196
+ */
5197
+ if (IsA (tle -> expr , Var ))
5198
+ {
5199
+ Var * var = (Var * ) tle -> expr ;
5200
+
5201
+ varno = var -> varno ;
5202
+ colno = var -> varattno ;
5203
+ }
5204
+ }
5179
5205
5180
- varno = var -> varno ;
5181
- colno = var -> varattno ;
5206
+ if (varno > 0 )
5207
+ {
5208
+ EState * estate = fsstate -> ss .ps .state ;
5209
+ RangeTblEntry * rte = rt_fetch (varno , estate -> es_range_table );
5210
+
5211
+ relname = rte -> eref -> aliasname ;
5212
+
5213
+ if (colno == 0 )
5214
+ is_wholerow = true;
5215
+ else if (colno > 0 && colno <= list_length (rte -> eref -> colnames ))
5216
+ attname = strVal (list_nth (rte -> eref -> colnames , colno - 1 ));
5217
+ else if (colno == SelfItemPointerAttributeNumber )
5218
+ attname = "ctid" ;
5219
+ else if (colno == ObjectIdAttributeNumber )
5220
+ attname = "oid" ;
5182
5221
}
5183
5222
}
5184
-
5185
- if (varno > 0 )
5223
+ else if (rel )
5186
5224
{
5187
- EState * estate = fsstate -> ss . ps . state ;
5188
- RangeTblEntry * rte = rt_fetch ( varno , estate -> es_range_table );
5225
+ /* Non-ForeignScan case (we should always have a rel here) */
5226
+ TupleDesc tupdesc = RelationGetDescr ( rel );
5189
5227
5190
- relname = rte -> eref -> aliasname ;
5228
+ relname = RelationGetRelationName (rel );
5229
+ if (errpos -> cur_attno > 0 && errpos -> cur_attno <= tupdesc -> natts )
5230
+ {
5231
+ Form_pg_attribute attr = TupleDescAttr (tupdesc ,
5232
+ errpos -> cur_attno - 1 );
5191
5233
5192
- if (colno == 0 )
5193
- is_wholerow = true;
5194
- else if (colno > 0 && colno <= list_length (rte -> eref -> colnames ))
5195
- attname = strVal (list_nth (rte -> eref -> colnames , colno - 1 ));
5196
- else if (colno == SelfItemPointerAttributeNumber )
5234
+ attname = NameStr (attr -> attname );
5235
+ }
5236
+ else if (errpos -> cur_attno == SelfItemPointerAttributeNumber )
5197
5237
attname = "ctid" ;
5198
- else if (colno == ObjectIdAttributeNumber )
5199
- attname = "oid" ;
5200
5238
}
5201
5239
5202
5240
if (relname && is_wholerow )
0 commit comments