Sin eval
es requerido. Su problema es que no está decodificando el valor como un objeto json.
CREATE OR REPLACE FUNCTION json_update(data json, key text, value json)
RETURNS json AS
$BODY$
from json import loads, dumps
if key is None: return data
js = loads(data)
# you must decode 'value' with loads too:
js[key] = loads(value)
return dumps(js)
$BODY$
LANGUAGE plpythonu VOLATILE;
postgres=# SELECT json_update('{"a":1}', 'a', '{"innerkey":"innervalue"}');
json_update
-----------------------------------
{"a": {"innerkey": "innervalue"}}
(1 row)
No solo eso, sino usar eval
para decodificar json
es peligroso y poco fiable. No es confiable porque json
no es Python, simplemente se evalúa un poco como él la mayor parte del tiempo. No es seguro porque nunca sabes lo que podrías estar evaluando. En este caso, está protegido en gran medida por el analizador json de PostgreSQL:
postgres=# SELECT json_update(
postgres(# '{"a":1}',
postgres(# 'a',
postgres(# '__import__(''shutil'').rmtree(''/glad_this_is_not_just_root'')'
postgres(# );
ERROR: invalid input syntax for type json
LINE 4: '__import__(''shutil'').rmtree(''/glad_this_is_not_...
^
DETAIL: Token "__import__" is invalid.
CONTEXT: JSON data, line 1: __import__...
... pero no me sorprendería en absoluto si alguien puede deslizar un eval
explotar más allá de eso. Entonces, la lección aquí:no use eval
.