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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers