Thread

  1. Re: foreign key on virtual generated column

    Srinath Reddy Sadipiralla <srinath2133@gmail.com> — 2025-12-02T15:03:09Z

    Hi Jian,
    
    while continuing my review , i observed these:
    
    1) if we have multiple virtual generated columns in table,
    and foreign key on a virtual generated column, during
    RI check in RI_FKey_check, i observed that we are
    computing other virtual generated columns which are
    not part of foreign key,which i think is (not too) slow but
    slowness depends on how complex the generated
    expressions are, number of virtual generated columns
    in the table ,i think we can use riinfo inside
    ExecComputeGenerated to only compute the columns
    in foreign key.
    
                             Table "public.gtest23b"
     Column |  Type   | Collation | Nullable |            Default
    --------+---------+-----------+----------+-------------------------------
     a      | integer |           | not null |
     b      | bigint  |           |          | generated always as ((a * 1))
     c      | integer |           |          | generated always as ((a * 2))
    Indexes:
        "gtest23b_pkey" PRIMARY KEY, btree (a)
    Foreign-key constraints:
        "gtest23b_b_fkey" FOREIGN KEY (b) REFERENCES gtest23a(x) ON DELETE
    CASCADE
    
    when we do "insert into gtest23b values(3);"
    RI kicks in and computes the b,c columns
    to check the values of "b" exists in primary key,
    but here the virtual generated columns which
    are not part of the foreign key like "c" are also
    computed.
    
    2) during ALTER type or expression of a column
    which is involved in foreign key , we go through
    TryReuseForeignKey to determine whether
    revalidation of this constraint can be skipped, here
    i observed for this patch that if we saw a virtual
    generated column in foreign key we simply bail out
    and won't skip the revalidation, i have a doubt and
    an observation here
    
    doubt:- why are we simply bailing out and doing
    revalidation if we found out if there's a virtual
    generated column in foreign key? in TryReuseForeignKey,
    is it because if we change the expression obviously get
    new values ,so we anyway need to revalidate them with
    primary key but what about if virtual column's type gets
    ALTERed in such a way that the equality operator is same
    underneath like varchar(10) to varchar(40) , then i think
    we can allow the normal flow as we do with normal columns
    by appending the OID to old_conpfeqop as below.
    
    for (i = 0; i < numkeys; i++)
    con->old_conpfeqop = lappend_oid(con->old_conpfeqop, conpfeqop[i]);
    
    observation:- if we go with current logic of bailing out and
    doing the revalidation if a virtual gen column is present in
    foreign key, then even if we are only altering a standard
    column that is perfectly safe (binary compatible) like
    varchar(10) to varchar(40) and not doing any change
    to the virtual generated column in a composite foreign
    key, even then we are going to do revalidation and
    goes to validateForeignKeyConstraint which is
    kind of slow, as the values in foreign key are same
    as before even after ALTER, so its not needed to
    revalidate again primary key, and if we try to simulate
    the same scenario without the virtual generated column
    and instead a normal column then postgres skips the revalidation,
    because the ALTERed column is a standard column
    and the type underneath is "compatible",so there won't
    be any changes to the values in foreign key to revalidate
    with the primary key, so it's skipped.
    
    for example:
    
    CREATE TABLE parent (
        id_std text,
        id_extra int,
        PRIMARY KEY (id_std, id_extra)
    );
    
    INSERT INTO parent VALUES ('A', 1), ('B', 2);
    
    
    Scenario 1:  No Virtual Columns
    Here, we have a composite FK, but both columns are standard.
    We alter 'col_std' (varchar 10 -> 40).
    Expectation: Optimization SUCCEEDS. Validation SKIPPED.
    
    CREATE TABLE child_a (
        col_std varchar(10),
        col_extra int,
        FOREIGN KEY (col_std, col_extra) REFERENCES parent(id_std, id_extra)
    );
    
    INSERT INTO child_a VALUES ('A', 1);
    
    ALTER TABLE child_a ALTER COLUMN col_std TYPE varchar(40);
    
    
    Scenario 2: With Virtual Column
    Here, we have a composite FK where one column is Virtual.
    We alter 'col_std' (varchar 10 -> 50). 'col_virt' is Untouched.
    Expectation: Optimization FAILS. Validation FORCED (Slow).
    
    CREATE TABLE child_b (
        col_std varchar(10),
        col_virt int GENERATED ALWAYS AS (1) VIRTUAL,
        FOREIGN KEY (col_std, col_virt) REFERENCES parent_comp(id_std, id_extra)
    );
    
    INSERT INTO child_b VALUES ('A');
    
    This should trigger the "validateForeignKeyConstraint" path because
    TryReuseForeignKey bails out.
    ALTER TABLE child_b ALTER COLUMN col_std TYPE varchar(40);
    
    -- 
    Thanks,
    Srinath Reddy Sadipiralla
    EDB: https://www.enterprisedb.com/