Improving a function that UPSERTs based on an input array

依然范特西╮ 提交于 2019-12-02 07:04:40

I think that your code is fine. To answer your questions:

  • Since you are not using dynamic SQL, you are automatically safe from SQL injection.

  • Using anyarray is tricky and usually only works well with C functions. You may be able to get it to work, but it would be complicated (for example, involve metadata queries).

    Your idea to supply incomplete rows would need to solve some difficulties:

    • How would you tell which array element belongs to which column, if some columns are skipped?

    If you want to UPDATE only certain columns in the conflict case, you could supply an additional upd_cols varbit argument that says which columns should be updated.

    The update for the fourth column could then look like this:

    SET col = CASE WHEN get_bit(upd_cols, 3) = 1
                   THEN EXCLUDED.col
                   ELSE item.col
              END
    
  • I don't see any advantage in using transaction control inside your code. This would decrease the usefulness of the functionprocedure, because the caller might want to call your function and later roll back the action because of some problem.

  • I would RETURN void (or use a procedure), because the caller already has the information how big the array is. This is what normal DML statements do.

We've got a lot of different servers pushing up to central tables in Postgres, which adds another wrinkle. What if I add a column to my table:

ALTER TABLE item ADD COLUMN category citext;

Now the table has four columns instead of three.

All of my existing pushes immediately break because now there's a column missing from the inputs. There is a 0% chance that we can update all of the server simultaneously, so that's no an option.

One solution is to create a custom type for each version of the table:

CREATE TYPE item_v1 AS (
    id uuid,
    marked_for_deletion boolean,
    name_ citext);

CREATE TYPE item_v2 AS (
    id uuid,
    marked_for_deletion boolean,
    name_ citext,
    category citext);

And then a function for each type:

CREATE OR REPLACE FUNCTION data.item_insert_array (data_in item_v1[]) 
etc.

CREATE OR REPLACE FUNCTION data.item_insert_array (data_in item_v2[]) 
etc.

I guess you could have a single ginormous method that takes anyarray and uses a CASE to sort out what code to run. I wouldn't do that for a few reasons, but I suppose you could. (I've seen that approach turn gangrenous in more than one language in a real hurry.)

All of that seems like a fair bit of work. Is there a simpler technique I'm missing? I'm imagining that you could submit structured text/XML/JSON, unpack it and work from there. But I would not file that under "simpler."

I'm still working through the design here, obviously. I've written up enough code to test out what I've shown, but want to sort out the details before going back and implementing this on dozens of tables.

Thanks for any help.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!