Search code examples
jsonpostgresqlplpgsqlpostgresql-9.6node-postgres

How to insert into a table with not null columns using json?


I am trying to insert into a table food with multiple not-null default columns, with commands like:

  • food_insertone('{"id": 1, "taste": "sweet"}'::JSON)
  • food_insertone('{"id": 2}'::JSON)
  • food_insertone('{"id": 3, "taste": null}'::JSON)

And the result should be like:

INSERTED 1, 'sweet'
INSERTED 2, ''
ERROR (null not allowed in taste)

The table food is defined as:

CREATE TABLE "food" (
  "id"    INT,
  "taste" TEXT NOT NULL DEFAULT '',
  ...
);

CREATE OR REPLACE FUNCTION "food_insertone" (JSON)
RETURNS VOID AS $$
  INSERT INTO "food" SELECT * FROM json_populate_record(NULL::"food", $1);
$$ LANGUAGE SQL;

And i am trying to insert as:

SELECT food_insertone('{"id": 1}'::JSON);

But this doesnt work and gives me an error:

null value in column "taste" violates not-null constraint

I understand that json_populate_record() creates NULL values for columns which are not mentioned in the JSON, which is causing NULL to be inserted, and thus this error. A plain insert would work, but this is a dynamic table.


Solution

  • to use the default value simple case:

    t=# create table food(id int, t text not null default 'some');
    CREATE TABLE
    t=# insert into food(id) SELECT id FROM json_populate_record(NULL::"food", '{"id":0}');
    INSERT 0 1
    t=# select * from food ;
     id |  t
    ----+------
      0 | some
    (1 row)
    

    using coalesce and another value:

    t=# insert into food(id,t) 
    SELECT id,coalesce(t,'some simple other value') 
    FROM json_populate_record(NULL::"food", '{"id":0}');   
    

    and of course you can use some monstrous way to get actual default value in :

    t=# insert into food(id,t) SELECT id,coalesce(t,rtrim) FROM json_populate_record(NULL::"food", '{"id":0}') join (select rtrim(ltrim(split_part(column_default,'::',1),$$'$$),$$'$$) from information_schema.columns where table_name = 'food' and column_name = 't') dflt on true;
    INSERT 0 1
    t=# select * from food ;
     id |            t
    ----+-------------------------
      0 | some simple other value
      0 | some
    (2 rows)