Thread

  1. Re: BUG #19099: Conditional DELETE from partitioned table with non-updatable partition raises internal error

    amit <amitlangote09@gmail.com> — 2025-10-31T00:30:39Z

    On Thu, Oct 30, 2025 at 10:48 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
    > Kirill Reshke <reshkekirill@gmail.com> writes:
    > > Tom wrote:
    > >> It's surely pretty accidental (and arguably not desirable)
    > >> if "DELETE FROM pt WHERE false" doesn't fail the same way.
    >
    > > I cannot prove to myself why failing here is actually desirable. Can
    > > you elaborate?
    >
    > If we throw that failure in some cases but not others, we're exposing
    > implementation details.
    >
    > The definition could have been "throw 'cannot delete from foreign
    > table' only if the query actually attempts to delete some specific
    > row from some foreign table", but it is not implemented that way.
    > Instead the error is thrown during query startup if the query has
    > a foreign table as a potential delete target.  Thus, as things stand
    > today, you might or might not get the error depending on whether
    > the planner can prove that that partition won't be deleted from.
    > This is not a great user experience, because we don't (and won't)
    > make any hard promises about how smart the planner is.
    >
    > An analogy perhaps is that whether you get a "permission denied"
    > error about some target table is not conditional on whether the
    > query actually attempts to delete any rows from it.  We go out
    > of our way to make sure that that happens when required by spec,
    > even if the planner is able to prove that no delete will happen.
    >
    > None of this is meant to justify throwing an internal error here;
    > that's clearly bad.  I'm just saying that there would be little
    > wrong with fixing it by throwing "cannot delete" instead.  The user
    > has no right to expect that that won't happen in a case like this.
    
    We might be able to throw the "cannot delete from foreign table" like this:
    
    @@ -987,6 +987,16 @@ add_row_identity_columns(PlannerInfo *root, Index rtindex,
    
             fdwroutine = GetFdwRoutineForRelation(target_relation, false);
    
    +        if (fdwroutine->ExecForeignDelete == NULL)
    +            ereport(ERROR,
    +                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    +                     errmsg("cannot delete from foreign table \"%s\"",
    +                            RelationGetRelationName(target_relation))));
    +        if (fdwroutine->ExecForeignUpdate == NULL)
    +            ereport(ERROR,
    +                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    +                     errmsg("cannot update foreign table \"%s\"",
    +                            RelationGetRelationName(target_relation))));
             if (fdwroutine->AddForeignUpdateTargets != NULL)
                 fdwroutine->AddForeignUpdateTargets(root, rtindex,
                                                     target_rte, target_relation);
    
    but I am not sure how consistent the following is after applying that:
    
    postgres=# set enable_partition_pruning to off;
    SET
    postgres=# EXPLAIN verbose DELETE FROM pt WHERE false;
    ERROR:  cannot delete from foreign table "p1"
    postgres=# set enable_partition_pruning to on;
    SET
    
    -- we don't even hit the foreign table in the planner
    postgres=# EXPLAIN verbose DELETE FROM pt WHERE false;
                          QUERY PLAN
    -------------------------------------------------------
     Delete on public.pt  (cost=0.00..0.00 rows=0 width=0)
       ->  Result  (cost=0.00..0.00 rows=0 width=0)
             Output: ctid
             Replaces: Scan on pt
             One-Time Filter: false
    (5 rows)
    
    -- 
    Thanks, Amit Langote