Inserting a COALESCE(NULL,default)

前端 未结 3 896
逝去的感伤
逝去的感伤 2020-12-11 16:39

I have tables that use UUIDs. I want to be able to insert a new row with or without a UUID as sometimes the client will generate the UUID other times it won\'t.

Each

3条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-12-11 17:11

    The core problem is the special nature of the key word DEFAULT in a VALUES expression attached to an INSERT. Per documentation:

    In a VALUES list appearing at the top level of an INSERT, an expression can be replaced by DEFAULT to indicate that the destination column's default value should be inserted. DEFAULT cannot be used when VALUES appears in other contexts.

    Bold emphasis mine. Specifically, DEFAULT cannot be argument to a function:

    COALESCE(function_parameter, DEFAULT)  -- not possible

    Possible solution

    There are various ways, depending on exact requirements.

    This function doesn't need to know the actual default of person.id - which seems to be what you are after:

    CREATE OR REPLACE FUNCTION create_person(_id UUID)
      RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS
    $func$
    BEGIN
       IF _id IS NULL THEN                 -- no UUID provided
          INSERT INTO myschema.person(id)  -- see below about schema name
          VALUES (DEFAULT);                -- use the key word DEFAULT
       ELSE                                -- UUID provided
          INSERT INTO myschema.person(id)
          VALUES (_id);
       END IF;
    
       RETURN FOUND;                       -- (return value pointless so far)
    END
    $func$;
    

    Avoid using the same name for parameters and involved table columns. Since function parameters are visible inside every SQL command in the function body, it can lead to very confusing naming conflicts (even if target columns of INSERT are exempt from this in modern Postgres). I use _id as parameter name instead.

    Default values for other columns not mentioned in the INSERT are filled in automatically. I use the key word DEFAULT because we are required to list at least one target column for the INSERT.

    The boolean return value is pointless in this demo because it is always true (unless you have triggers that might skip the row).

    Related answer with possible alternatives and a lot of explanation:

    • Generate DEFAULT values in a CTE UPSERT using PostgreSQL 9.3

    Aside: You should schema-qualify all function and table names in a SECURITY DEFINER function - or (probably better if you aren't sure) explicitly set the search_path to defend against possible attacks. More:

    • How does the search_path influence identifier resolution and the "current schema"

提交回复
热议问题