Avoid doing catalog lookups in postgres_fdw's conversion_error_callback.
As in 50371df26, this is a bad idea since the callback can't really know what error is being thrown and thus whether or not it is safe to attempt catalog accesses. Rather than pushing said accesses into the mainline code where they'd usually be a waste of cycles, we can look at the query's rangetable instead. This change does mean that we'll be printing query aliases (if any were used) rather than the table or column's true name. But that doesn't seem like a bad thing: it's certainly a more useful definition in self-join cases, for instance. In any case, it seems unlikely that any applications would be depending on this detail, so it seems safe to change. Patch by me. Original complaint by Andres Freund; Bharath Rupireddy noted the connection to conversion_error_callback. Discussion: https://postgr.es/m/20210106020229.ne5xnuu6wlondjpe@alap3.anarazel.de
This commit is contained in:
parent
ab2e19987f
commit
c7b7311f61
@ -4096,15 +4096,17 @@ DROP TABLE reind_fdw_parent;
|
|||||||
-- conversion error
|
-- conversion error
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE int;
|
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE int;
|
||||||
SELECT * FROM ft1 WHERE c1 = 1; -- ERROR
|
SELECT * FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8) WHERE x1 = 1; -- ERROR
|
||||||
ERROR: invalid input syntax for type integer: "foo"
|
ERROR: invalid input syntax for type integer: "foo"
|
||||||
CONTEXT: column "c8" of foreign table "ft1"
|
CONTEXT: column "x8" of foreign table "ftx"
|
||||||
SELECT ft1.c1, ft2.c2, ft1.c8 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR
|
SELECT ftx.x1, ft2.c2, ftx.x8 FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
|
||||||
|
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
|
||||||
ERROR: invalid input syntax for type integer: "foo"
|
ERROR: invalid input syntax for type integer: "foo"
|
||||||
CONTEXT: column "c8" of foreign table "ft1"
|
CONTEXT: column "x8" of foreign table "ftx"
|
||||||
SELECT ft1.c1, ft2.c2, ft1 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR
|
SELECT ftx.x1, ft2.c2, ftx FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
|
||||||
|
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
|
||||||
ERROR: invalid input syntax for type integer: "foo"
|
ERROR: invalid input syntax for type integer: "foo"
|
||||||
CONTEXT: whole-row reference to foreign table "ft1"
|
CONTEXT: whole-row reference to foreign table "ftx"
|
||||||
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
|
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
|
||||||
ERROR: invalid input syntax for type integer: "foo"
|
ERROR: invalid input syntax for type integer: "foo"
|
||||||
CONTEXT: processing expression at position 2 in select list
|
CONTEXT: processing expression at position 2 in select list
|
||||||
|
@ -302,16 +302,8 @@ typedef struct
|
|||||||
*/
|
*/
|
||||||
typedef struct ConversionLocation
|
typedef struct ConversionLocation
|
||||||
{
|
{
|
||||||
Relation rel; /* foreign table's relcache entry. */
|
|
||||||
AttrNumber cur_attno; /* attribute number being processed, or 0 */
|
AttrNumber cur_attno; /* attribute number being processed, or 0 */
|
||||||
|
ForeignScanState *fsstate; /* plan node being processed */
|
||||||
/*
|
|
||||||
* In case of foreign join push down, fdw_scan_tlist is used to identify
|
|
||||||
* the Var node corresponding to the error location and
|
|
||||||
* fsstate->ss.ps.state gives access to the RTEs of corresponding relation
|
|
||||||
* to get the relation name and attribute name.
|
|
||||||
*/
|
|
||||||
ForeignScanState *fsstate;
|
|
||||||
} ConversionLocation;
|
} ConversionLocation;
|
||||||
|
|
||||||
/* Callback argument for ec_member_matches_foreign */
|
/* Callback argument for ec_member_matches_foreign */
|
||||||
@ -7082,7 +7074,6 @@ make_tuple_from_result_row(PGresult *res,
|
|||||||
/*
|
/*
|
||||||
* Set up and install callback to report where conversion error occurs.
|
* Set up and install callback to report where conversion error occurs.
|
||||||
*/
|
*/
|
||||||
errpos.rel = rel;
|
|
||||||
errpos.cur_attno = 0;
|
errpos.cur_attno = 0;
|
||||||
errpos.fsstate = fsstate;
|
errpos.fsstate = fsstate;
|
||||||
errcallback.callback = conversion_error_callback;
|
errcallback.callback = conversion_error_callback;
|
||||||
@ -7185,34 +7176,32 @@ make_tuple_from_result_row(PGresult *res,
|
|||||||
/*
|
/*
|
||||||
* Callback function which is called when error occurs during column value
|
* Callback function which is called when error occurs during column value
|
||||||
* conversion. Print names of column and relation.
|
* conversion. Print names of column and relation.
|
||||||
|
*
|
||||||
|
* Note that this function mustn't do any catalog lookups, since we are in
|
||||||
|
* an already-failed transaction. Fortunately, we can get the needed info
|
||||||
|
* from the query's rangetable instead.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
conversion_error_callback(void *arg)
|
conversion_error_callback(void *arg)
|
||||||
{
|
{
|
||||||
|
ConversionLocation *errpos = (ConversionLocation *) arg;
|
||||||
|
ForeignScanState *fsstate = errpos->fsstate;
|
||||||
|
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
|
||||||
|
int varno = 0;
|
||||||
|
AttrNumber colno = 0;
|
||||||
const char *attname = NULL;
|
const char *attname = NULL;
|
||||||
const char *relname = NULL;
|
const char *relname = NULL;
|
||||||
bool is_wholerow = false;
|
bool is_wholerow = false;
|
||||||
ConversionLocation *errpos = (ConversionLocation *) arg;
|
|
||||||
|
|
||||||
if (errpos->rel)
|
if (fsplan->scan.scanrelid > 0)
|
||||||
{
|
{
|
||||||
/* error occurred in a scan against a foreign table */
|
/* error occurred in a scan against a foreign table */
|
||||||
TupleDesc tupdesc = RelationGetDescr(errpos->rel);
|
varno = fsplan->scan.scanrelid;
|
||||||
Form_pg_attribute attr = TupleDescAttr(tupdesc, errpos->cur_attno - 1);
|
colno = errpos->cur_attno;
|
||||||
|
|
||||||
if (errpos->cur_attno > 0 && errpos->cur_attno <= tupdesc->natts)
|
|
||||||
attname = NameStr(attr->attname);
|
|
||||||
else if (errpos->cur_attno == SelfItemPointerAttributeNumber)
|
|
||||||
attname = "ctid";
|
|
||||||
|
|
||||||
relname = RelationGetRelationName(errpos->rel);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* error occurred in a scan against a foreign join */
|
/* error occurred in a scan against a foreign join */
|
||||||
ForeignScanState *fsstate = errpos->fsstate;
|
|
||||||
ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan);
|
|
||||||
EState *estate = fsstate->ss.ps.state;
|
|
||||||
TargetEntry *tle;
|
TargetEntry *tle;
|
||||||
|
|
||||||
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
|
tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist,
|
||||||
@ -7220,37 +7209,42 @@ conversion_error_callback(void *arg)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Target list can have Vars and expressions. For Vars, we can get
|
* Target list can have Vars and expressions. For Vars, we can get
|
||||||
* its relation, however for expressions we can't. Thus for
|
* some information, however for expressions we can't. Thus for
|
||||||
* expressions, just show generic context message.
|
* expressions, just show generic context message.
|
||||||
*/
|
*/
|
||||||
if (IsA(tle->expr, Var))
|
if (IsA(tle->expr, Var))
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte;
|
|
||||||
Var *var = (Var *) tle->expr;
|
Var *var = (Var *) tle->expr;
|
||||||
|
|
||||||
rte = exec_rt_fetch(var->varno, estate);
|
varno = var->varno;
|
||||||
|
colno = var->varattno;
|
||||||
if (var->varattno == 0)
|
|
||||||
is_wholerow = true;
|
|
||||||
else
|
|
||||||
attname = get_attname(rte->relid, var->varattno, false);
|
|
||||||
|
|
||||||
relname = get_rel_name(rte->relid);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varno > 0)
|
||||||
|
{
|
||||||
|
EState *estate = fsstate->ss.ps.state;
|
||||||
|
RangeTblEntry *rte = exec_rt_fetch(varno, estate);
|
||||||
|
|
||||||
|
relname = rte->eref->aliasname;
|
||||||
|
|
||||||
|
if (colno == 0)
|
||||||
|
is_wholerow = true;
|
||||||
|
else if (colno > 0 && colno <= list_length(rte->eref->colnames))
|
||||||
|
attname = strVal(list_nth(rte->eref->colnames, colno - 1));
|
||||||
|
else if (colno == SelfItemPointerAttributeNumber)
|
||||||
|
attname = "ctid";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relname && is_wholerow)
|
||||||
|
errcontext("whole-row reference to foreign table \"%s\"", relname);
|
||||||
|
else if (relname && attname)
|
||||||
|
errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
|
||||||
else
|
else
|
||||||
errcontext("processing expression at position %d in select list",
|
errcontext("processing expression at position %d in select list",
|
||||||
errpos->cur_attno);
|
errpos->cur_attno);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relname)
|
|
||||||
{
|
|
||||||
if (is_wholerow)
|
|
||||||
errcontext("whole-row reference to foreign table \"%s\"", relname);
|
|
||||||
else if (attname)
|
|
||||||
errcontext("column \"%s\" of foreign table \"%s\"", attname, relname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find an equivalence class member expression to be computed as a sort column
|
* Find an equivalence class member expression to be computed as a sort column
|
||||||
* in the given target.
|
* in the given target.
|
||||||
|
@ -1129,9 +1129,11 @@ DROP TABLE reind_fdw_parent;
|
|||||||
-- conversion error
|
-- conversion error
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE int;
|
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE int;
|
||||||
SELECT * FROM ft1 WHERE c1 = 1; -- ERROR
|
SELECT * FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8) WHERE x1 = 1; -- ERROR
|
||||||
SELECT ft1.c1, ft2.c2, ft1.c8 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR
|
SELECT ftx.x1, ft2.c2, ftx.x8 FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
|
||||||
SELECT ft1.c1, ft2.c2, ft1 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR
|
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
|
||||||
|
SELECT ftx.x1, ft2.c2, ftx FROM ft1 ftx(x1,x2,x3,x4,x5,x6,x7,x8), ft2
|
||||||
|
WHERE ftx.x1 = ft2.c1 AND ftx.x1 = 1; -- ERROR
|
||||||
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
|
SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
|
||||||
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
|
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user