According to the following documentation on IterateForeignScan() in
50.2. Foreign Data Wrapper Callback Routines, I have created a patch to
support the error handling in file_fdw. Please find attached a patch.
Note that PostgreSQL's executor doesn't care whether the rows
returned violate the NOT NULL constraints which were defined
on the foreign table columns - but the planner does care, and
may optimize queries incorrectly if NULL values are present
in a column declared not to contain them. If a NULL value is
encountered when the user has declared that none should be
present, it may be appropriate to raise an error (just as you
would need to do in the case of a data type mismatch).
Best regards,
Etsuro Fujita
*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
***************
*** 502,507 **** fileIterateForeignScan(ForeignScanState *node)
--- 502,510 ----
{
FileFdwExecutionState *festate = (FileFdwExecutionState *)
node->fdw_state;
TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
+ Relation rel = node->ss.ss_currentRelation;
+ TupleConstr *constr = rel->rd_att->constr;
+ bool has_not_null = (constr != NULL) ? constr->has_not_null
: false;
bool found;
ErrorContextCallback errcontext;
***************
*** 528,534 **** fileIterateForeignScan(ForeignScanState *node)
--- 531,556 ----
slot->tts_values,
slot->tts_isnull,
NULL);
if (found)
+ {
ExecStoreVirtualTuple(slot);
+ if (has_not_null)
+ {
+ int natts = rel->rd_att->natts;
+ int attrChk;
+
+ for (attrChk = 1; attrChk <= natts; attrChk++)
+ {
+ if (rel->rd_att->attrs[attrChk - 1]->attnotnull
&&
+ slot_attisnull(slot, attrChk))
+ ereport(ERROR,
+
(errcode(ERRCODE_NOT_NULL_VIOLATION),
+ errmsg("null value in
column \"%s\" violates not-null constraint",
+
NameStr(rel->rd_att->attrs[attrChk - 1]->attname)),
+ errdetail("Failing row
contains %s.",
+
ExecBuildSlotValueDescription(slot, 64))));
+ }
+ }
+ }
/* Remove error callback. */
error_context_stack = errcontext.previous;
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 86,93 **** static void ExecutePlan(EState *estate, PlanState *planstate,
DestReceiver *dest);
static bool ExecCheckRTEPerms(RangeTblEntry *rte);
static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
- static char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
-
int maxfieldlen);
static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
Plan *planTree);
static void OpenIntoRel(QueryDesc *queryDesc);
--- 86,91 ----
***************
*** 1604,1610 **** ExecConstraints(ResultRelInfo *resultRelInfo,
* here since heap field values could be very long, whereas index entries
* typically aren't so wide.
*/
! static char *
ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
{
StringInfoData buf;
--- 1602,1608 ----
* here since heap field values could be very long, whereas index entries
* typically aren't so wide.
*/
! char *
ExecBuildSlotValueDescription(TupleTableSlot *slot, int maxfieldlen)
{
StringInfoData buf;
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
***************
*** 184,189 **** extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate,
Oid relid);
--- 184,191 ----
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate);
+ extern char *ExecBuildSlotValueDescription(TupleTableSlot *slot,
+
int maxfieldlen);
extern ExecRowMark *ExecFindRowMark(EState *estate, Index rti);
extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List
*targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers