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

Reply via email to