2

This question relates to Update dynamic column names using keys from jsonb argument and my code is based on the answer provided there by https://dba.stackexchange.com/users/3684/erwin-brandstetter

I'm trying to write a generic function that inserts data into a table with multiple data types from a jsonb record. How can I dynamically cast each value to the data type for the respective column?

I'm using PostgreSQL version 15.2

A typical call to this function would be:

json_data = '{
 "text_field": "Some text",
 "numeric_field": 17,
 "datetime_field": "2023年04月24日 11:06:50.36758-04",
 "boolean_field": true
}'
SELECT * FROM insert_jsonb('schema_name', 'table_name', json_data);

My function:

CREATE OR REPLACE FUNCTION insert_from_jsonb(
 sch_name text,
 tbl_name text,
 js jsonb,
 out new_id uuid;
) AS
$func$
DECLARE
 _sql text;
BEGIN
 SELECT INTO _sql
 'INSERT INTO ' || sch_name || '.' || tbl_name || ' ('
 || string_agg(format('%I', key), ', ') || ') VALUES ('
 || string_agg(format('%L', value), ', ') || ')' -- < how can I cast each value?
 || ' RETURNING uuid'
 FROM jsonb_each_text(js);
 IF _sql IS NOT NULL THEN
 -- RAISE NOTICE '%', _sql; -- uncomment for debugging
 EXECUTE _sql INTO new_id; -- comment out for debugging
 END IF;
END
$func$ LANGUAGE plpgsql ;
asked Apr 24, 2023 at 19:47
0

2 Answers 2

2

You concatenate values as (untyped) string literals into the query string, so you don't need to add an explicit cast. Postgres will coerce each value with an assignment cast to the data type of the target column automatically. (Must be valid for the type, of course. Else you get an error. But an explicit cast wouldn't get you the same result.)

Your posted function works as is. I would only suggest an outer format() to tidy up:

CREATE OR REPLACE FUNCTION insert_from_jsonb(sch_name text
 , tbl_name text
 , js jsonb
 , OUT new_id uuid)
 LANGUAGE plpgsql AS
$func$
DECLARE
 _sql text;
BEGIN
 SELECT INTO _sql
 format('INSERT INTO %I.%I (%s) VALUES (%s) RETURNING uuid'
 , sch_name, tbl_name
 , string_agg(format('%I', key), ', ')
 , string_agg(format('%L', value), ', ') -- < no explicit cast needed
 )
 FROM jsonb_each_text(js);
 IF _sql IS NULL THEN
 RAISE WARNING '%', 'Your text here';
 ELSE
 -- RAISE NOTICE '%', _sql; -- uncomment for debugging
 EXECUTE _sql INTO new_id; -- comment out for debugging
 END IF;
END
$func$;
answered Apr 25, 2023 at 3:28
1
  • Thank you Erwin, much appreciated! Commented Apr 25, 2023 at 13:31
1

See the updated insert function in Erwin's answer. I decided to merge the insert with the update function from the related question: Update dynamic column names using keys from jsonb argument. This function updates an existing record if the "id" is provided, or generates a new UUID and inserts a new record if no "id" is provided. Either way returns the resource ID.

CREATE OR REPLACE FUNCTION upsert_from_jsonb(
 sch_name text,
 tbl_name text,
 js jsonb,
 out res_id uuid)
LANGUAGE plpgsql AS
$func$
DECLARE
 new_js jsonb;
 _sql text;
BEGIN
 SELECT (js ->> 'id')::uuid INTO res_id;
 IF res_id IS NOT NULL THEN
 new_js := js - 'id';
 SELECT INTO _sql
 format('UPDATE %I.%I SET %s WHERE id = %L',
 sch_name, tbl_name,
 string_agg(format('%I = %L', key, value), ', '),
 res_id
 )
 FROM jsonb_each_text(new_js);
 ELSE
 res_id := gen_random_uuid();
 new_js := js || jsonb_build_object('id', res_id);
 SELECT INTO _sql
 format('INSERT INTO %I.%I (%s) VALUES (%s)',
 sch_name, tbl_name,
 string_agg(format('%I', key), ', '),
 string_agg(format('%L', value), ', ')
 )
 FROM jsonb_each_text(new_js);
 END IF;
 IF _sql IS NULL THEN
 RAISE WARNING 'No sql for %I.%I on %s', sch_name, tbl_name, js;
 ELSE
 -- RAISE NOTICE '%', _sql; -- uncomment for debugging
 EXECUTE _sql; -- comment out for debugging
 END IF;
END
$func$;
answered Apr 25, 2023 at 16:12

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.