None of the currently posted answers works fully correctly, they fail on some of the tests from RFC 7396 (https://www.rfc-editor.org/rfc/rfc7396#section-2).
Here is a solution that passes all RFC 7396 test cases, structurally based on jsonb_recursive_merge from accepted answer in question Merging JSONB values in PostgreSQL?.
CREATE OR REPLACE FUNCTION jsonb_mergepatch(
target jsonb, -- target JSON value
patch jsonb -- patch JSON value
)
RETURNS jsonb LANGUAGE plpgsql IMMUTABLE AS
$$
BEGIN
-- If the patch is not a JSON object, return the patch as the result (base case)
IF patch isnull or jsonb_typeof(patch) != 'object' THEN
RETURN patch;
END IF;
-- If the target is not an object, set it to an empty object
IF target isnull or jsonb_typeof(target) != 'object' THEN
target := '{}';
END IF;
RETURN coalesce(
jsonb_object_agg(
coalesce(targetKey, patchKey), -- there will be either one or both keys equal
CASE
WHEN patchKey isnull THEN targetValue -- key missing in patch - retain target value
ELSE jsonb_mergepatch(targetValue, patchValue)
END
),
'{}'::jsonb -- if SELECT will return no keys (empty table), then jsonb_object_agg will return NULL, need to return {} in that case
)
FROM jsonb_each(target) temp1(targetKey, targetValue)
FULL JOIN jsonb_each(patch) temp2(patchKey, patchValue) ON targetKey = patchKey
WHERE jsonb_typeof(patchValue) != 'null' OR patchValue isnull; -- remove keys which are set to null in patch object
END;
$$;
The correctness can be tested by running test cases from RFC 7396 against above function:
with tests (a, b, expected_result) as (
values
-- RFC 7396 test cases: https://www.rfc-editor.org/rfc/rfc7396#appendix-A
('{"a": "b"}'::jsonb, '{"a": "c"}'::jsonb, '{"a": "c"}'::jsonb ),
('{"a": "b"}'::jsonb, '{"b": "c"}'::jsonb, '{"a": "b", "b": "c"}'::jsonb),
('{"a": "b"}'::jsonb, '{"a": null}'::jsonb, '{}'::jsonb ),
('{"a": "b", "b": "c"}'::jsonb, '{"a": null}'::jsonb, '{"b": "c"}'::jsonb ),
('{"a": ["b"]}'::jsonb, '{"a": "c"}'::jsonb, '{"a": "c"}'::jsonb ),
('{"a": "c"}'::jsonb, '{"a": ["b"]}'::jsonb, '{"a": ["b"]}'::jsonb ),
('{"a": {"b": "c"}}'::jsonb, '{"a": {"b": "d", "c": null}}'::jsonb, '{"a": {"b": "d"}}'::jsonb ),
('{"a": [{"b": "c"}]}'::jsonb, '{"a": [1]}'::jsonb, '{"a": [1]}'::jsonb ),
('["a", "b"]'::jsonb, '["c", "d"]'::jsonb, '["c", "d"]'::jsonb ),
('{"a": "b"}'::jsonb, '["c"]'::jsonb, '["c"]'::jsonb ),
('{"a": "foo"}'::jsonb, 'null'::jsonb, 'null'::jsonb ),
('{"a": "foo"}'::jsonb, '"bar"'::jsonb, '"bar"'::jsonb ),
('{"e": null}'::jsonb, '{"a": 1}'::jsonb, '{"e": null, "a": 1}'::jsonb ),
('[1, 2]'::jsonb, '{"a": "b", "c": null}'::jsonb, '{"a": "b"}'::jsonb ),
('{}'::jsonb, '{"a": {"bb": {"ccc": null}}}'::jsonb, '{"a": {"bb": {}}}'::jsonb )
)
select tests.*,
jsonb_mergepatch(a, b) as actual_result,
jsonb_mergepatch(a, b)::varchar = expected_result::varchar as test_passed
from tests
jsonb_setis probably the closest you can get (don't really know what that Oracle function does). But without more details, this is really hard to answer. Please edit your question (by clicking on the edit link below it) and add some sample data and the expected output based on that data as formatted text. See here for some tips on how to create nice looking text tables.