postgres plpgsql插入jsonb而不转义双引号
postgres plpgsql insert jsonb without escaping double quotes
我正在使用 psycopg2
与 postgres (v13) 数据库交互。我打算动态创建 sql 脚本来创建 tables、函数等,以便在数据库中执行。
我创建了脚本来对具有两个 boolean
和一个 jsonb
列的 table 执行更新插入。剧本和玩具table在这个db<>fiddle。它完美运行。
问题是当我尝试使用 psycopg2
动态地获得相同的结果时。我创建了一个 'toy' 示例。下面的设置代码创建了一个简单的 3 列 table,其中包含 2 个布尔值和一个 jsonb 列。连接设置加上一些 sql 脚本来创建 upsert 函数(如上面 fiddle 中使用的)和 call/test 函数:
from psycopg2 import connect
from psycopg2.extras import Json
import pandas as pd
conn_graphstruct = connect(host='localhost', database='graphstruct_data',
port='5665', user='postgres', password='postgres')
cursor_graphstruct = conn_graphstruct.cursor()
def graph_script(x):
cursor = cursor_graphstruct
conn = conn_graphstruct
try:
cursor.execute(x)
conn.commit()
except Exception as e:
print(e)
conn.rollback()
def graph_query(x):
temp = pd.read_sql(x, conn_graphstruct)
if not temp.empty:
return temp
def graph_fetch(x):
cursor = cursor_graphstruct
conn = conn_graphstruct
try:
cursor.execute(x)
records = cursor.fetchall()
return records
except Exception as e:
print(e)
make_table = '''
drop table if exists graphy;
create temporary table graphy (
directed boolean,
multigraph boolean,
graph jsonb);
create unique index unique_db_name on graphy ((graph->>'name'));
insert into graphy(directed, multigraph, graph) values(FALSE,FALSE, '{"node_default": {},
"edge_default": {}, "name": "test_8"}');
'''
make_procedure = '''
drop procedure if exists p_upsert_meta();
CREATE OR REPLACE PROCEDURE p_upsert_meta(x bool, y bool, z jsonb) LANGUAGE plpgsql
as $$
begin
INSERT into graphy (directed, multigraph, graph) values (x,y,z)
ON CONFLICT ((graph->>'name'))
DO update
set (directed, multigraph, graph) = (x,y,z);
END
$$;
'''
run_procedure = '''
call p_upsert_meta(FALSE,TRUE, '{"node_default": {}, "edge_default": {},
"name": "test_10"}');
call p_upsert_meta(FALSE,TRUE, '{"node_default": {}, "edge_default": {},
"name": "test_10"}');
'''
然后我运行脚本。首先使用定义的脚本,然后使用动态 sql 方法编写查询。
graph_script(make_table)
graph_script(make_procedure)
graph_script(run_procedure)
directed = False
multi = False
graph_name = 'proto_1'
graph = '{"node_default": Null, "edge_default": True,"name": "' + graph_name + '"}'
print(graph)
graph_script('call p_upsert_meta({},{},{})'.format(directed, multi, Json(graph)))
graph_script('call p_upsert_meta({},{},{})'.format(directed, multi, Json(graph)))
应该 结果是 table 中的三个条目。而是查询 jsonb 列 (graph
):
query = '''
select graph
from graphy
'''
graph_fetch(query)
结果:
{"node_default": Null, "edge_default": True,"name": "proto_1"} -- composed string
[({'name': 'test_8', 'edge_default': {}, 'node_default': {}},),
({'name': 'test_10', 'edge_default': {}, 'node_default': {}},),
('{"node_default": Null, "edge_default": True,"name": "proto_1"}',), -- text not jsonb
('{"node_default": Null, "edge_default": True,"name": "proto_1"}',)] -- text not jsonb
如果我不使用 psycopg2.extras.Json,我会收到此错误 - syntax error at or near "{" LINE 1: call p_upsert_meta(False,False,{"node_default": Null, "edge_...
。但是使用 Json
转义所有双引号。发生这种情况时 CONFLICT
将不起作用。我试过在函数中使用 z
和 graph
并使用 import json
方法。
我错过了什么来正确插入没有转义双引号的 jsonb?
备注:
- 是的,我必须使用 psycopg2
发生这种情况是因为您将字符串传递给 Json
。 Json
将 Python 类型改编为 Postgres 中的 Json(b)。一个简单的例子:
cur.execute('insert into json_test (fld_jsonb) values(%s)', [Json({"node_default": None, "edge_default": True,"name": "graph_name"})])
cur.execute('insert into json_test (fld_jsonb) values(%s)', [Json('{"node_default": None, "edge_default": True,"name": "graph_name"}')])
con.commit()
cur.execute("select fld_jsonb from json_test")
rs = cur.fetchall()
# Python dict as Json value
rs[0][0]
{'name': 'graph_name', 'edge_default': True, 'node_default': None}
# Python string as Json value
rs[1][0]
'{"node_default": None, "edge_default": True,"name": "graph_name"}'
如果您希望 Json
适应正常工作,请使用适当的 Python 类型:list
用于 json array
和 dict
用于 json object
.这也适用于包含的类型,因此 None
而不是 Python 侧的 Null
。
更新
如果您想使用字符串,请不要使用 Json
改编:
cur.execute('insert into json_test (fld_jsonb) values(%s)', ['{"node_default": null, "edge_default": true,"name": "graph_name"}'])
con.commit()
rs[2][0]
{'name': 'graph_name', 'edge_default': True, 'node_default': None}
不过您将需要使用 JSON 值,例如null
和 true
而不是 None
和 True
.
我正在使用 psycopg2
与 postgres (v13) 数据库交互。我打算动态创建 sql 脚本来创建 tables、函数等,以便在数据库中执行。
我创建了脚本来对具有两个 boolean
和一个 jsonb
列的 table 执行更新插入。剧本和玩具table在这个db<>fiddle。它完美运行。
问题是当我尝试使用 psycopg2
动态地获得相同的结果时。我创建了一个 'toy' 示例。下面的设置代码创建了一个简单的 3 列 table,其中包含 2 个布尔值和一个 jsonb 列。连接设置加上一些 sql 脚本来创建 upsert 函数(如上面 fiddle 中使用的)和 call/test 函数:
from psycopg2 import connect
from psycopg2.extras import Json
import pandas as pd
conn_graphstruct = connect(host='localhost', database='graphstruct_data',
port='5665', user='postgres', password='postgres')
cursor_graphstruct = conn_graphstruct.cursor()
def graph_script(x):
cursor = cursor_graphstruct
conn = conn_graphstruct
try:
cursor.execute(x)
conn.commit()
except Exception as e:
print(e)
conn.rollback()
def graph_query(x):
temp = pd.read_sql(x, conn_graphstruct)
if not temp.empty:
return temp
def graph_fetch(x):
cursor = cursor_graphstruct
conn = conn_graphstruct
try:
cursor.execute(x)
records = cursor.fetchall()
return records
except Exception as e:
print(e)
make_table = '''
drop table if exists graphy;
create temporary table graphy (
directed boolean,
multigraph boolean,
graph jsonb);
create unique index unique_db_name on graphy ((graph->>'name'));
insert into graphy(directed, multigraph, graph) values(FALSE,FALSE, '{"node_default": {},
"edge_default": {}, "name": "test_8"}');
'''
make_procedure = '''
drop procedure if exists p_upsert_meta();
CREATE OR REPLACE PROCEDURE p_upsert_meta(x bool, y bool, z jsonb) LANGUAGE plpgsql
as $$
begin
INSERT into graphy (directed, multigraph, graph) values (x,y,z)
ON CONFLICT ((graph->>'name'))
DO update
set (directed, multigraph, graph) = (x,y,z);
END
$$;
'''
run_procedure = '''
call p_upsert_meta(FALSE,TRUE, '{"node_default": {}, "edge_default": {},
"name": "test_10"}');
call p_upsert_meta(FALSE,TRUE, '{"node_default": {}, "edge_default": {},
"name": "test_10"}');
'''
然后我运行脚本。首先使用定义的脚本,然后使用动态 sql 方法编写查询。
graph_script(make_table)
graph_script(make_procedure)
graph_script(run_procedure)
directed = False
multi = False
graph_name = 'proto_1'
graph = '{"node_default": Null, "edge_default": True,"name": "' + graph_name + '"}'
print(graph)
graph_script('call p_upsert_meta({},{},{})'.format(directed, multi, Json(graph)))
graph_script('call p_upsert_meta({},{},{})'.format(directed, multi, Json(graph)))
应该 结果是 table 中的三个条目。而是查询 jsonb 列 (graph
):
query = '''
select graph
from graphy
'''
graph_fetch(query)
结果:
{"node_default": Null, "edge_default": True,"name": "proto_1"} -- composed string
[({'name': 'test_8', 'edge_default': {}, 'node_default': {}},),
({'name': 'test_10', 'edge_default': {}, 'node_default': {}},),
('{"node_default": Null, "edge_default": True,"name": "proto_1"}',), -- text not jsonb
('{"node_default": Null, "edge_default": True,"name": "proto_1"}',)] -- text not jsonb
如果我不使用 psycopg2.extras.Json,我会收到此错误 - syntax error at or near "{" LINE 1: call p_upsert_meta(False,False,{"node_default": Null, "edge_...
。但是使用 Json
转义所有双引号。发生这种情况时 CONFLICT
将不起作用。我试过在函数中使用 z
和 graph
并使用 import json
方法。
我错过了什么来正确插入没有转义双引号的 jsonb?
备注:
- 是的,我必须使用 psycopg2
发生这种情况是因为您将字符串传递给 Json
。 Json
将 Python 类型改编为 Postgres 中的 Json(b)。一个简单的例子:
cur.execute('insert into json_test (fld_jsonb) values(%s)', [Json({"node_default": None, "edge_default": True,"name": "graph_name"})])
cur.execute('insert into json_test (fld_jsonb) values(%s)', [Json('{"node_default": None, "edge_default": True,"name": "graph_name"}')])
con.commit()
cur.execute("select fld_jsonb from json_test")
rs = cur.fetchall()
# Python dict as Json value
rs[0][0]
{'name': 'graph_name', 'edge_default': True, 'node_default': None}
# Python string as Json value
rs[1][0]
'{"node_default": None, "edge_default": True,"name": "graph_name"}'
如果您希望 Json
适应正常工作,请使用适当的 Python 类型:list
用于 json array
和 dict
用于 json object
.这也适用于包含的类型,因此 None
而不是 Python 侧的 Null
。
更新
如果您想使用字符串,请不要使用 Json
改编:
cur.execute('insert into json_test (fld_jsonb) values(%s)', ['{"node_default": null, "edge_default": true,"name": "graph_name"}'])
con.commit()
rs[2][0]
{'name': 'graph_name', 'edge_default': True, 'node_default': None}
不过您将需要使用 JSON 值,例如null
和 true
而不是 None
和 True
.