From 21deb81acbb0516e2f8d3934f40f951b8c7d3345 Mon Sep 17 00:00:00 2001 From: khanova <32508607+khanova@users.noreply.github.com> Date: Thu, 12 Oct 2023 14:32:49 +0200 Subject: [PATCH] Fix case for array of jsons (#5523) ## Problem Currently proxy doesn't handle array of json parameters correctly. ## Summary of changes Added one more level of quotes escaping for the array of jsons case. Resolves: https://github.com/neondatabase/neon/issues/5515 --- proxy/src/http/sql_over_http.rs | 31 +++++++++++++++++++++++++++++-- test_runner/regress/test_proxy.py | 8 +++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/proxy/src/http/sql_over_http.rs b/proxy/src/http/sql_over_http.rs index 63d7d807e9..02ce3aa9dc 100644 --- a/proxy/src/http/sql_over_http.rs +++ b/proxy/src/http/sql_over_http.rs @@ -102,9 +102,9 @@ fn json_array_to_pg_array(value: &Value) -> Result, serde_json::E // convert to text with escaping Value::Bool(_) => serde_json::to_string(value).map(Some), Value::Number(_) => serde_json::to_string(value).map(Some), - Value::Object(_) => serde_json::to_string(value).map(Some), // here string needs to be escaped, as it is part of the array + Value::Object(_) => json_array_to_pg_array(&Value::String(serde_json::to_string(value)?)), Value::String(_) => serde_json::to_string(value).map(Some), // recurse into array @@ -613,7 +613,7 @@ fn _pg_array_parse( } } } - '}' => { + '}' if !quote => { level -= 1; if level == 0 { push_checked(&mut entry, &mut entries, elem_type)?; @@ -697,6 +697,14 @@ mod tests { "{{true,false},{NULL,42},{\"foo\",\"bar\\\"-\\\\\"}}".to_owned() )] ); + // array of objects + let json = r#"[{"foo": 1},{"bar": 2}]"#; + let json: Value = serde_json::from_str(json).unwrap(); + let pg_params = json_to_pg_text(vec![json]).unwrap(); + assert_eq!( + pg_params, + vec![Some(r#"{"{\"foo\":1}","{\"bar\":2}"}"#.to_owned())] + ); } #[test] @@ -824,4 +832,23 @@ mod tests { json!([[[1, 2, 3], [4, 5, 6]]]) ); } + #[test] + fn test_pg_array_parse_json() { + fn pt(pg_arr: &str) -> Value { + pg_array_parse(pg_arr, &Type::JSONB).unwrap() + } + assert_eq!(pt(r#"{"{}"}"#), json!([{}])); + assert_eq!( + pt(r#"{"{\"foo\": 1, \"bar\": 2}"}"#), + json!([{"foo": 1, "bar": 2}]) + ); + assert_eq!( + pt(r#"{"{\"foo\": 1}", "{\"bar\": 2}"}"#), + json!([{"foo": 1}, {"bar": 2}]) + ); + assert_eq!( + pt(r#"{{"{\"foo\": 1}", "{\"bar\": 2}"}}"#), + json!([[{"foo": 1}, {"bar": 2}]]) + ); + } } diff --git a/test_runner/regress/test_proxy.py b/test_runner/regress/test_proxy.py index c542ab05ae..f57b15c9b9 100644 --- a/test_runner/regress/test_proxy.py +++ b/test_runner/regress/test_proxy.py @@ -188,7 +188,7 @@ def test_sql_over_http(static_proxy: NeonProxy): headers={"Content-Type": "application/sql", "Neon-Connection-String": connstr}, verify=str(static_proxy.test_output_dir / "proxy.crt"), ) - assert response.status_code == 200 + assert response.status_code == 200, response.text return response.json() rows = q("select 42 as answer")["rows"] @@ -206,6 +206,12 @@ def test_sql_over_http(static_proxy: NeonProxy): rows = q("select $1::json->'a' as answer", [{"a": {"b": 42}}])["rows"] assert rows == [{"answer": {"b": 42}}] + rows = q("select $1::jsonb[] as answer", [[{}]])["rows"] + assert rows == [{"answer": [{}]}] + + rows = q("select $1::jsonb[] as answer", [[{"foo": 1}, {"bar": 2}]])["rows"] + assert rows == [{"answer": [{"foo": 1}, {"bar": 2}]}] + rows = q("select * from pg_class limit 1")["rows"] assert len(rows) == 1